X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FItems.pm;h=939e68241b8c12afc2072f56dc00c130345d9805;hb=c2cee4b445a46dda5b85c65ff742593a5125e73a;hp=a0d2a4bac4d842868287fb97039aaa39abb3cebd;hpb=6e714ce442c3f09f61685c8fc3d6bb14ecd89dd8;p=koha_gimpoz diff --git a/C4/Items.pm b/C4/Items.pm index a0d2a4bac4..939e68241b 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -1,6 +1,7 @@ package C4::Items; # Copyright 2007 LibLime, Inc. +# Parts Copyright Biblibre 2010 # # This file is part of Koha. # @@ -13,12 +14,14 @@ package C4::Items; # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License along with -# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, -# Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. use strict; +#use warnings; FIXME - Bug 2505 +use Carp; use C4::Context; use C4::Koha; use C4::Biblio; @@ -29,6 +32,7 @@ use C4::Log; use C4::Branch; require C4::Reserves; use C4::Charset; +use C4::Acquisition; use vars qw($VERSION @ISA @EXPORT); @@ -45,6 +49,7 @@ BEGIN { AddItem AddItemBatchFromMarc ModItemFromMarc + Item2Marc ModItem ModDateLastSeen ModItemTransfer @@ -60,8 +65,15 @@ BEGIN { GetItemInfosOf GetItemsByBiblioitemnumber GetItemsInfo + GetItemsLocationInfo get_itemnumbers_of GetItemnumberFromBarcode + GetBarcodeFromItemnumber + + DelItemCheck + MoveItemFromBiblio + GetLatestAcquisitions + CartToShelf ); } @@ -108,11 +120,7 @@ of C =head2 GetItem -=over 4 - -$item = GetItem($itemnumber,$barcode,$serial); - -=back + $item = GetItem($itemnumber,$barcode,$serial); Return item information, for a given itemnumber or barcode. The return value is a hashref mapping item column @@ -142,7 +150,6 @@ sub GetItem { my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?"); $ssth->execute($data->{'itemnumber'}) ; ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array(); - warn $data->{'serialseq'} , $data->{'publisheddate'}; } #if we don't have an items.itype, use biblioitems.itemtype. if( ! $data->{'itype'} ) { @@ -153,14 +160,34 @@ sub GetItem { return $data; } # sub GetItem -=head2 AddItemFromMarc +=head2 CartToShelf -=over 4 + CartToShelf($itemnumber); -my ($biblionumber, $biblioitemnumber, $itemnumber) - = AddItemFromMarc($source_item_marc, $biblionumber); +Set the current shelving location of the item record +to its stored permanent shelving location. This is +primarily used to indicate when an item whose current +location is a special processing ('PROC') or shelving cart +('CART') location is back in the stacks. -=back +=cut + +sub CartToShelf { + my ( $itemnumber ) = @_; + + unless ( $itemnumber ) { + croak "FAILED CartToShelf() - no itemnumber supplied"; + } + + my $item = GetItem($itemnumber); + $item->{location} = $item->{permanent_location}; + ModItem($item, undef, $itemnumber); +} + +=head2 AddItemFromMarc + + my ($biblionumber, $biblioitemnumber, $itemnumber) + = AddItemFromMarc($source_item_marc, $biblionumber); Given a MARC::Record object containing an embedded item record and a biblionumber, create a new item record. @@ -173,19 +200,19 @@ sub AddItemFromMarc { # parse item hash from MARC my $frameworkcode = GetFrameworkCode( $biblionumber ); - my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode ); - my $unlinked_item_subfields = _get_unlinked_item_subfields($source_item_marc, $frameworkcode); + my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode); + + my $localitemmarc=MARC::Record->new; + $localitemmarc->append_fields($source_item_marc->field($itemtag)); + my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items'); + my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode); return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields); } =head2 AddItem -=over 4 - -my ($biblionumber, $biblioitemnumber, $itemnumber) - = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]); - -=back + my ($biblionumber, $biblioitemnumber, $itemnumber) + = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]); Given a hash containing item column names as keys, create a new Koha item record. @@ -224,6 +251,12 @@ sub AddItem { _set_derived_columns_for_add($item); $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields); # FIXME - checks here + unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype + my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?"); + $itype_sth->execute( $item->{'biblionumber'} ); + ( $item->{'itype'} ) = $itype_sth->fetchrow_array; + } + my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} ); $item->{'itemnumber'} = $itemnumber; @@ -238,11 +271,8 @@ sub AddItem { =head2 AddItemBatchFromMarc -=over 4 - -($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, $biblionumber, $biblioitemnumber, $frameworkcode); - -=back + ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, + $biblionumber, $biblioitemnumber, $frameworkcode); Efficiently create item records from a MARC biblio record with embedded item fields. This routine is suitable for batch jobs. @@ -260,7 +290,7 @@ This function returns an arrayref of new itemsnumbers and an arrayref of item errors encountered during the processing. Each entry in the errors list is a hashref containing the following keys: -=over 2 +=over =item item_sequence @@ -345,39 +375,81 @@ sub AddItemBatchFromMarc { =head2 ModItemFromMarc -=over 4 - -ModItemFromMarc($item_marc, $biblionumber, $itemnumber); - -=back + ModItemFromMarc($item_marc, $biblionumber, $itemnumber); This function updates an item record based on a supplied C object containing an embedded item field. This API is meant for the use of C; for other purposes, C should be used. +This function uses the hash %default_values_for_mod_from_marc, +which contains default values for item fields to +apply when modifying an item. This is needed beccause +if an item field's value is cleared, TransformMarcToKoha +does not include the column in the +hash that's passed to ModItem, which without +use of this hash makes it impossible to clear +an item field's value. See bug 2466. + +Note that only columns that can be directly +changed from the cataloging and serials +item editors are included in this hash. + =cut +my %default_values_for_mod_from_marc = ( + barcode => undef, + booksellerid => undef, + ccode => undef, + 'items.cn_source' => undef, + copynumber => undef, + damaged => 0, +# dateaccessioned => undef, + enumchron => undef, + holdingbranch => undef, + homebranch => undef, + itemcallnumber => undef, + itemlost => 0, + itemnotes => undef, + itype => undef, + location => undef, + materials => undef, + notforloan => 0, + paidfor => undef, + price => undef, + replacementprice => undef, + replacementpricedate => undef, + restricted => undef, + stack => undef, + stocknumber => undef, + uri => undef, + wthdrawn => 0, +); + sub ModItemFromMarc { my $item_marc = shift; my $biblionumber = shift; my $itemnumber = shift; - my $dbh = C4::Context->dbh; - my $frameworkcode = GetFrameworkCode( $biblionumber ); - my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode ); - my $unlinked_item_subfields = _get_unlinked_item_subfields($item_marc, $frameworkcode); - + my $dbh = C4::Context->dbh; + my $frameworkcode = GetFrameworkCode($biblionumber); + my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode ); + + my $localitemmarc = MARC::Record->new; + $localitemmarc->append_fields( $item_marc->field($itemtag) ); + my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' ); + foreach my $item_field ( keys %default_values_for_mod_from_marc ) { + $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field}); + } + my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode ); + return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields); } =head2 ModItem -=over 4 - -ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]); - -=back + ModItem({ column => $newvalue }, $biblionumber, + $itemnumber[, $original_item_marc]); Change one or more columns in an item record and update the MARC representation of the item. @@ -448,11 +520,7 @@ sub ModItem { =head2 ModItemTransfer -=over 4 - -ModItemTransfer($itenumber, $frombranch, $tobranch); - -=back + ModItemTransfer($itenumber, $frombranch, $tobranch); Marks an item as being transferred from one branch to another. @@ -477,11 +545,7 @@ sub ModItemTransfer { =head2 ModDateLastSeen -=over 4 - -ModDateLastSeen($itemnum); - -=back + ModDateLastSeen($itemnum); Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking. C<$itemnum> is the item number @@ -497,11 +561,7 @@ sub ModDateLastSeen { =head2 DelItem -=over 4 - -DelItem($biblionumber, $itemnumber); - -=back + DelItem($dbh, $biblionumber, $itemnumber); Exported function (core API) for deleting an item record in Koha. @@ -538,8 +598,6 @@ sub DelItem { =head2 CheckItemPreSave -=over 4 - my $item_ref = TransformMarcToKoha($marc, 'items'); # do stuff my %errors = CheckItemPreSave($item_ref); @@ -553,8 +611,6 @@ sub DelItem { print "item is OK"; } -=back - Given a hashref containing item fields, determine if it can be inserted or updated in the database. Specifically, checks for database integrity issues, and returns a hash containing any @@ -637,11 +693,7 @@ has copy-and-paste work. =head2 GetItemStatus -=over 4 - -$itemstatushash = GetItemStatus($fwkcode); - -=back + $itemstatushash = GetItemStatus($fwkcode); Returns a list of valid values for the C field. @@ -656,32 +708,24 @@ Create a status selector with the following code =head3 in PERL SCRIPT -=over 4 - -my $itemstatushash = getitemstatus; -my @itemstatusloop; -foreach my $thisstatus (keys %$itemstatushash) { - my %row =(value => $thisstatus, - statusname => $itemstatushash->{$thisstatus}->{'statusname'}, - ); - push @itemstatusloop, \%row; -} -$template->param(statusloop=>\@itemstatusloop); - -=back + my $itemstatushash = getitemstatus; + my @itemstatusloop; + foreach my $thisstatus (keys %$itemstatushash) { + my %row =(value => $thisstatus, + statusname => $itemstatushash->{$thisstatus}->{'statusname'}, + ); + push @itemstatusloop, \%row; + } + $template->param(statusloop=>\@itemstatusloop); =head3 in TEMPLATE -=over 4 - - - -=back + =cut @@ -719,7 +763,6 @@ sub GetItemStatus { while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { $itemstatus{$authorisedvalue} = $lib; } - $authvalsth->finish; return \%itemstatus; exit 1; } @@ -728,7 +771,6 @@ sub GetItemStatus { #No authvalue list # build default } - $sth->finish; } #No authvalue list @@ -739,11 +781,7 @@ sub GetItemStatus { =head2 GetItemLocation -=over 4 - -$itemlochash = GetItemLocation($fwk); - -=back + $itemlochash = GetItemLocation($fwk); Returns a list of valid values for the C field. @@ -756,34 +794,26 @@ Create a location selector with the following code =head3 in PERL SCRIPT -=over 4 - -my $itemlochash = getitemlocation; -my @itemlocloop; -foreach my $thisloc (keys %$itemlochash) { - my $selected = 1 if $thisbranch eq $branch; - my %row =(locval => $thisloc, - selected => $selected, - locname => $itemlochash->{$thisloc}, - ); - push @itemlocloop, \%row; -} -$template->param(itemlocationloop => \@itemlocloop); - -=back + my $itemlochash = getitemlocation; + my @itemlocloop; + foreach my $thisloc (keys %$itemlochash) { + my $selected = 1 if $thisbranch eq $branch; + my %row =(locval => $thisloc, + selected => $selected, + locname => $itemlochash->{$thisloc}, + ); + push @itemlocloop, \%row; + } + $template->param(itemlocationloop => \@itemlocloop); =head3 in TEMPLATE -=over 4 - - - -=back + =cut @@ -819,7 +849,6 @@ sub GetItemLocation { while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { $itemlocation{$authorisedvalue} = $lib; } - $authvalsth->finish; return \%itemlocation; exit 1; } @@ -828,7 +857,6 @@ sub GetItemLocation { #No authvalue list # build default } - $sth->finish; } #No authvalue list @@ -839,11 +867,7 @@ sub GetItemLocation { =head2 GetLostItems -=over 4 - -$items = GetLostItems( $where, $orderby ); - -=back + $items = GetLostItems( $where, $orderby ); This function gets a list of lost items. @@ -867,9 +891,9 @@ from the "items" table as keys. =item usage in the perl script: -my $where = { barcode => '0001548' }; -my $items = GetLostItems( $where, "homebranch" ); -$template->param( itemsloop => $items ); + my $where = { barcode => '0001548' }; + my $items = GetLostItems( $where, "homebranch" ); + $template->param( itemsloop => $items ); =back @@ -883,23 +907,24 @@ sub GetLostItems { my $query = " SELECT * - FROM items, biblio, authorised_values + FROM items + LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber) + LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber) + LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value) WHERE - items.biblionumber = biblio.biblionumber - AND items.itemlost = authorised_values.authorised_value - AND authorised_values.category = 'LOST' + authorised_values.category = 'LOST' AND itemlost IS NOT NULL AND itemlost <> 0 - "; my @query_parameters; foreach my $key (keys %$where) { $query .= " AND $key LIKE ?"; push @query_parameters, "%$where->{$key}%"; } - if ( defined $orderby ) { - $query .= ' ORDER BY ?'; - push @query_parameters, $orderby; + my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/; + + if ( defined $orderby && grep($orderby, @ordervalues)) { + $query .= ' ORDER BY '.$orderby; } my $sth = $dbh->prepare($query); @@ -913,11 +938,9 @@ sub GetLostItems { =head2 GetItemsForInventory -=over 4 - -$itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size); - -=back + $itemlist = GetItemsForInventory($minlocation, $maxlocation, + $location, $itemtype $datelastseen, $branch, + $offset, $size, $statushash); Retrieve a list of title/authors/barcode/callnumber, for biblio inventory. @@ -928,45 +951,75 @@ seen. It is ordered by callnumber then title. The required minlocation & maxlocation parameters are used to specify a range of item callnumbers the datelastseen can be used to specify that you want to see items not seen since a past date only. offset & size can be used to retrieve only a part of the whole listing (defaut behaviour) +$statushash requires a hashref that has the authorized values fieldname (intems.notforloan, etc...) as keys, and an arrayref of statuscodes we are searching for as values. =cut sub GetItemsForInventory { - my ( $minlocation, $maxlocation,$location, $itemtype, $datelastseen, $branch, $offset, $size ) = @_; + my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_; my $dbh = C4::Context->dbh; + my ( @bind_params, @where_strings ); my $query = <<'END_SQL'; -SELECT itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen +SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen FROM items LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber -WHERE itemcallnumber >= ? - AND itemcallnumber <= ? END_SQL - my @bind_params = ( $minlocation, $maxlocation ); + if ($statushash){ + for my $authvfield (keys %$statushash){ + if ( scalar @{$statushash->{$authvfield}} > 0 ){ + my $joinedvals = join ',', @{$statushash->{$authvfield}}; + push @where_strings, "$authvfield in (" . $joinedvals . ")"; + } + } + } + + if ($minlocation) { + push @where_strings, 'itemcallnumber >= ?'; + push @bind_params, $minlocation; + } + + if ($maxlocation) { + push @where_strings, 'itemcallnumber <= ?'; + push @bind_params, $maxlocation; + } if ($datelastseen) { $datelastseen = format_date_in_iso($datelastseen); - $query .= ' AND (datelastseen < ? OR datelastseen IS NULL) '; + push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)'; push @bind_params, $datelastseen; } if ( $location ) { - $query.= ' AND items.location = ? '; + push @where_strings, 'items.location = ?'; push @bind_params, $location; } - - if ( $branch ) { - $query.= ' AND items.homebranch = ? '; - push @bind_params, $branch; + + if ( $branchcode ) { + if($branch eq "homebranch"){ + push @where_strings, 'items.homebranch = ?'; + }else{ + push @where_strings, 'items.holdingbranch = ?'; + } + push @bind_params, $branchcode; } if ( $itemtype ) { - $query.= ' AND biblioitems.itemtype = ? '; + push @where_strings, 'biblioitems.itemtype = ?'; push @bind_params, $itemtype; } - $query .= ' ORDER BY itemcallnumber, title'; + if ( $ignoreissued) { + $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber "; + push @where_strings, 'issues.date_due IS NULL'; + } + + if ( @where_strings ) { + $query .= 'WHERE '; + $query .= join ' AND ', @where_strings; + } + $query .= ' ORDER BY items.cn_sort, itemcallnumber, title'; my $sth = $dbh->prepare($query); $sth->execute( @bind_params ); @@ -985,10 +1038,7 @@ END_SQL =head2 GetItemsCount -=over 4 -$count = &GetItemsCount( $biblionumber); - -=back + $count = &GetItemsCount( $biblionumber); This function return count of item with $biblionumber @@ -1003,17 +1053,12 @@ sub GetItemsCount { my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my $count = $sth->fetchrow; - $sth->finish; return ($count); } =head2 GetItemInfosOf -=over 4 - -GetItemInfosOf(@itemnumbers); - -=back + GetItemInfosOf(@itemnumbers); =cut @@ -1030,11 +1075,7 @@ sub GetItemInfosOf { =head2 GetItemsByBiblioitemnumber -=over 4 - -GetItemsByBiblioitemnumber($biblioitemnumber); - -=back + GetItemsByBiblioitemnumber($biblioitemnumber); Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP Called by C @@ -1066,7 +1107,6 @@ sub GetItemsByBiblioitemnumber { # set date_due to blank, so in the template we check itemlost, and wthdrawn $data->{'date_due'} = ''; } # else - $sth2->finish; # Find the last 3 people who borrowed this item. my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ? AND old_issues.borrowernumber = borrowers.borrowernumber @@ -1080,20 +1120,14 @@ sub GetItemsByBiblioitemnumber { $data->{"borrower$i2"} = $data2->{'borrowernumber'}; $i2++; } - $sth2->finish; push(@results,$data); } - $sth->finish; return (\@results); } =head2 GetItemsInfo -=over 4 - -@results = GetItemsInfo($biblionumber, $type); - -=back + @results = GetItemsInfo($biblionumber, $type); Returns information about books with the given biblionumber. @@ -1140,19 +1174,37 @@ If this is set, it is set to C. sub GetItemsInfo { my ( $biblionumber, $type ) = @_; my $dbh = C4::Context->dbh; - my $query = "SELECT *,items.notforloan as itemnotforloan - FROM items - LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber - LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber"; - $query .= (C4::Context->preference('item-level_itypes')) ? - " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype " - : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype "; - $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ; + # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance. + my $query = " + SELECT items.*, + biblio.*, + biblioitems.volume, + biblioitems.number, + biblioitems.itemtype, + biblioitems.isbn, + biblioitems.issn, + biblioitems.publicationyear, + biblioitems.publishercode, + biblioitems.volumedate, + biblioitems.volumedesc, + biblioitems.lccn, + biblioitems.url, + items.notforloan as itemnotforloan, + itemtypes.description, + itemtypes.notforloan as notforloan_per_itemtype, + branchurl + FROM items + LEFT JOIN branches ON items.homebranch = branches.branchcode + LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber + LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber + LEFT JOIN itemtypes ON itemtypes.itemtype = " + . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype'); + $query .= " WHERE items.biblionumber = ? ORDER BY branches.branchname,items.dateaccessioned desc" ; my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my $i = 0; my @results; - my ( $date_due, $count_reserves, $serial ); + my $serial; my $isth = $dbh->prepare( "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode @@ -1162,6 +1214,7 @@ sub GetItemsInfo { my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); while ( my $data = $sth->fetchrow_hashref ) { my $datedue = ''; + my $count_reserves; $isth->execute( $data->{'itemnumber'} ); if ( my $idata = $isth->fetchrow_hashref ) { $data->{borrowernumber} = $idata->{borrowernumber}; @@ -1171,7 +1224,7 @@ sub GetItemsInfo { $datedue = $idata->{'date_due'}; if (C4::Context->preference("IndependantBranches")){ my $userenv = C4::Context->userenv; - if ( ($userenv) && ( $userenv->{flags} != 1 ) ) { + if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch}); } } @@ -1184,12 +1237,11 @@ sub GetItemsInfo { if ( $datedue eq '' ) { my ( $restype, $reserves ) = C4::Reserves::CheckReserves( $data->{'itemnumber'} ); - if ($restype) { - $count_reserves = $restype; - } +# Previous conditional check with if ($restype) is not needed because a true +# result for one item will result in subsequent items defaulting to this true +# value. + $count_reserves = $restype; } - $isth->finish; - $ssth->finish; #get branch information..... my $bsth = $dbh->prepare( "SELECT * FROM branches WHERE branchcode = ? @@ -1223,7 +1275,31 @@ sub GetItemsInfo { my ($lib) = $sthnflstatus->fetchrow; $data->{notforloanvalue} = $lib; } - $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes')); + + # get restricted status and description if applicable + my $restrictedstatus = $dbh->prepare( + 'SELECT authorised_value + FROM marc_subfield_structure + WHERE kohafield="items.restricted" + ' + ); + + $restrictedstatus->execute; + ($authorised_valuecode) = $restrictedstatus->fetchrow; + if ($authorised_valuecode) { + $restrictedstatus = $dbh->prepare( + "SELECT lib,lib_opac FROM authorised_values + WHERE category=? + AND authorised_value=?" + ); + $restrictedstatus->execute( $authorised_valuecode, + $data->{restricted} ); + + if ( my $rstdata = $restrictedstatus->fetchrow_hashref ) { + $data->{restricted} = $rstdata->{'lib'}; + $data->{restrictedopac} = $rstdata->{'lib_opac'}; + } + } # my stack procedures my $stackstatus = $dbh->prepare( @@ -1251,6 +1327,7 @@ sub GetItemsInfo { my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers WHERE itemnumber = ? AND old_issues.borrowernumber = borrowers.borrowernumber + ORDER BY returndate DESC LIMIT 3"); $sth2->execute($data->{'itemnumber'}); my $ii = 0; @@ -1264,7 +1341,6 @@ sub GetItemsInfo { $results[$i] = $data; $i++; } - $sth->finish; if($serial) { return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results ); } else { @@ -1272,13 +1348,126 @@ sub GetItemsInfo { } } -=head2 get_itemnumbers_of +=head2 GetItemsLocationInfo + + my @itemlocinfo = GetItemsLocationInfo($biblionumber); + +Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question + +C returns a list of references-to-hash. Data returned: + +=over 2 + +=item C<$data-E{homebranch}> + +Branch Name of the item's homebranch + +=item C<$data-E{holdingbranch}> + +Branch Name of the item's holdingbranch + +=item C<$data-E{location}> + +Item's shelving location code + +=item C<$data-E{location_intranet}> + +The intranet description for the Shelving Location as set in authorised_values 'LOC' -=over 4 +=item C<$data-E{location_opac}> -my @itemnumbers_of = get_itemnumbers_of(@biblionumbers); +The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC +description is set. + +=item C<$data-E{itemcallnumber}> + +Item's itemcallnumber + +=item C<$data-E{cn_sort}> + +Item's call number normalized for sorting =back + +=cut + +sub GetItemsLocationInfo { + my $biblionumber = shift; + my @results; + + my $dbh = C4::Context->dbh; + my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch, + location, itemcallnumber, cn_sort + FROM items, branches as a, branches as b + WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode + AND biblionumber = ? + ORDER BY cn_sort ASC"; + my $sth = $dbh->prepare($query); + $sth->execute($biblionumber); + + while ( my $data = $sth->fetchrow_hashref ) { + $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location}); + $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1); + push @results, $data; + } + return @results; +} + + +=head2 GetLastAcquisitions + + my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), + 'itemtypes' => ('BK','BD')}, 10); + +=cut + +sub GetLastAcquisitions { + my ($data,$max) = @_; + + my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype'; + + my $number_of_branches = @{$data->{branches}}; + my $number_of_itemtypes = @{$data->{itemtypes}}; + + + my @where = ('WHERE 1 '); + $number_of_branches and push @where + , 'AND holdingbranch IN (' + , join(',', ('?') x $number_of_branches ) + , ')' + ; + + $number_of_itemtypes and push @where + , "AND $itemtype IN (" + , join(',', ('?') x $number_of_itemtypes ) + , ')' + ; + + my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned + FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber) + RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber) + @where + GROUP BY biblio.biblionumber + ORDER BY dateaccessioned DESC LIMIT $max"; + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare($query); + + $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}})); + + my @results; + while( my $row = $sth->fetchrow_hashref){ + push @results, {date => $row->{dateaccessioned} + , biblionumber => $row->{biblionumber} + , title => $row->{title}}; + } + + return @results; +} + +=head2 get_itemnumbers_of + + my @itemnumbers_of = get_itemnumbers_of(@biblionumbers); Given a list of biblionumbers, return the list of corresponding itemnumbers for each biblionumber. @@ -1313,11 +1502,7 @@ sub get_itemnumbers_of { =head2 GetItemnumberFromBarcode -=over 4 - -$result = GetItemnumberFromBarcode($barcode); - -=back + $result = GetItemnumberFromBarcode($barcode); =cut @@ -1332,14 +1517,30 @@ sub GetItemnumberFromBarcode { return ($result); } +=head2 GetBarcodeFromItemnumber + + $result = GetBarcodeFromItemnumber($itemnumber); + +=cut + +sub GetBarcodeFromItemnumber { + my ($itemnumber) = @_; + my $dbh = C4::Context->dbh; + + my $rq = + $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?"); + $rq->execute($itemnumber); + my ($result) = $rq->fetchrow; + return ($result); +} + =head3 get_item_authorised_values - find the types and values for all authorised values assigned to this item. +find the types and values for all authorised values assigned to this item. - parameters: - itemnumber +parameters: itemnumber - returns: a hashref malling the authorised value to the value set for this itemnumber +returns: a hashref malling the authorised value to the value set for this itemnumber $authorised_values = { 'CCODE' => undef, @@ -1355,7 +1556,7 @@ sub GetItemnumberFromBarcode { 'itemtypes' => 'SER', }; - Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level. +Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level. =cut @@ -1385,25 +1586,24 @@ sub get_item_authorised_values { =head3 get_authorised_value_images - find a list of icons that are appropriate for display based on the - authorised values for a biblio. +find a list of icons that are appropriate for display based on the +authorised values for a biblio. - parameters: listref of authorised values, such as comes from - get_item_ahtorised_values or - from C4::Biblio::get_biblio_authorised_values +parameters: listref of authorised values, such as comes from +get_item_authorised_values or +from C4::Biblio::get_biblio_authorised_values - returns: listref of hashrefs for each image. Each hashref looks like - this: +returns: listref of hashrefs for each image. Each hashref looks like this: { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif', label => '', category => '', value => '', } - Notes: Currently, I put on the full path to the images on the staff - side. This should either be configurable or not done at all. Since I - have to deal with 'intranet' or 'opac' in - get_biblio_authorised_values, perhaps I should be passing it in. +Notes: Currently, I put on the full path to the images on the staff +side. This should either be configurable or not done at all. Since I +have to deal with 'intranet' or 'opac' in +get_biblio_authorised_values, perhaps I should be passing it in. =cut @@ -1419,7 +1619,7 @@ sub get_authorised_value_images { && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) { # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) ); if ( defined $this_authorised_value->{'imageurl'} ) { - push @imagelist, { imageurl => C4::Koha::getitemtypeimagesrc( 'intranet' ) . '/' . $this_authorised_value->{'imageurl'}, + push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ), label => $this_authorised_value->{'lib'}, category => $this_authorised_value->{'category'}, value => $this_authorised_value->{'authorised_value'}, }; @@ -1444,11 +1644,7 @@ without careful thought. =head2 GetMarcItem -=over 4 - -my $item_marc = GetMarcItem($biblionumber, $itemnumber); - -=back + my $item_marc = GetMarcItem($biblionumber, $itemnumber); Returns MARC::Record of the item passed in parameter. This function is meant for use only in C, @@ -1476,19 +1672,28 @@ sub GetMarcItem { # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work. # Also, don't emit a subfield if the underlying field is blank. - my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } }; + + + return Item2Marc($itemrecord,$biblionumber); + +} +sub Item2Marc { + my ($itemrecord,$biblionumber)=@_; + my $mungeditem = { + map { + defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () + } keys %{ $itemrecord } + }; my $itemmarc = TransformKohaToMarc($mungeditem); + my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||''); my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'}); if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) { - my @fields = $itemmarc->fields(); - if ($#fields > -1) { - $fields[0]->add_subfields(@$unlinked_item_subfields); + foreach my $field ($itemmarc->field($itemtag)){ + $field->add_subfields(@$unlinked_item_subfields); } } - - return $itemmarc; - + return $itemmarc; } =head1 PRIVATE FUNCTIONS AND VARIABLES @@ -1526,11 +1731,7 @@ my %derived_columns = ( =head2 _set_derived_columns_for_add -=over 4 - -_set_derived_column_for_add($item); - -=back + _set_derived_column_for_add($item); Given an item hash representing a new item to be added, calculate any derived columns. Currently the only @@ -1554,11 +1755,7 @@ sub _set_derived_columns_for_add { =head2 _set_derived_columns_for_mod -=over 4 - -_set_derived_column_for_mod($item); - -=back + _set_derived_column_for_mod($item); Given an item hash representing a new item to be modified. calculate any derived columns. Currently the only @@ -1605,11 +1802,7 @@ sub _set_derived_columns_for_mod { =head2 _do_column_fixes_for_mod -=over 4 - -_do_column_fixes_for_mod($item); - -=back + _do_column_fixes_for_mod($item); Given an item hashref containing one or more columns to modify, fix up certain values. @@ -1639,15 +1832,17 @@ sub _do_column_fixes_for_mod { (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) { $item->{'wthdrawn'} = 0; } + if (exists $item->{'location'} && !exists $item->{'permanent_location'}) { + $item->{'permanent_location'} = $item->{'location'}; + } + if (exists $item->{'timestamp'}) { + delete $item->{'timestamp'}; + } } =head2 _get_single_item_column -=over 4 - -_get_single_item_column($column, $itemnumber); - -=back + _get_single_item_column($column, $itemnumber); Retrieves the value of a single column from an C row specified by C<$itemnumber>. @@ -1667,11 +1862,7 @@ sub _get_single_item_column { =head2 _calc_items_cn_sort -=over 4 - -_calc_items_cn_sort($item, $source_values); - -=back + _calc_items_cn_sort($item, $source_values); Helper routine to calculate C. @@ -1686,11 +1877,7 @@ sub _calc_items_cn_sort { =head2 _set_defaults_for_add -=over 4 - -_set_defaults_for_add($item_hash); - -=back + _set_defaults_for_add($item_hash); Given an item hash representing an item to be added, set correct default values for columns whose default value @@ -1725,29 +1912,13 @@ C sub _set_defaults_for_add { my $item = shift; - - # if dateaccessioned is provided, use it. Otherwise, set to NOW() - if (!(exists $item->{'dateaccessioned'}) || - ($item->{'dateaccessioned'} eq '')) { - # FIXME add check for invalid date - my $today = C4::Dates->new(); - $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues - } - - # various item status fields cannot be null - $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'} and $item->{'notforloan'} ne ''; - $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'} and $item->{'damaged'} ne ''; - $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'} and $item->{'itemlost'} ne ''; - $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'} and $item->{'wthdrawn'} ne ''; + $item->{dateaccessioned} ||= C4::Dates->new->output('iso'); + $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn)); } =head2 _koha_new_item -=over 4 - -my ($itemnumber,$error) = _koha_new_item( $item, $barcode ); - -=back + my ($itemnumber,$error) = _koha_new_item( $item, $barcode ); Perform the actual insert into the C table. @@ -1793,7 +1964,8 @@ sub _koha_new_item { uri = ?, enumchron = ?, more_subfields_xml = ?, - copynumber = ? + copynumber = ?, + stocknumber = ? "; my $sth = $dbh->prepare($query); $sth->execute( @@ -1830,22 +2002,137 @@ sub _koha_new_item { $item->{'enumchron'}, $item->{'more_subfields_xml'}, $item->{'copynumber'}, + $item->{'stocknumber'}, ); my $itemnumber = $dbh->{'mysql_insertid'}; if ( defined $sth->errstr ) { $error.="ERROR in _koha_new_item $query".$sth->errstr; } - $sth->finish(); return ( $itemnumber, $error ); } -=head2 _koha_modify_item +=head2 MoveItemFromBiblio -=over 4 + MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio); -my ($itemnumber,$error) =_koha_modify_item( $item ); +Moves an item from a biblio to another -=back +Returns undef if the move failed or the biblionumber of the destination record otherwise + +=cut + +sub MoveItemFromBiblio { + my ($itemnumber, $frombiblio, $tobiblio) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?"); + $sth->execute( $tobiblio ); + my ( $tobiblioitem ) = $sth->fetchrow(); + $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?"); + my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio); + if ($return == 1) { + + # Getting framework + my $frameworkcode = GetFrameworkCode($frombiblio); + + # Getting marc field for itemnumber + my ($itemtag, $itemsubfield) = GetMarcFromKohaField('items.itemnumber', $frameworkcode); + + # Getting the record we want to move the item from + my $record = GetMarcBiblio($frombiblio); + + # The item we want to move + my $item; + + # For each item + foreach my $fielditem ($record->field($itemtag)){ + # If it is the item we want to move + if ($fielditem->subfield($itemsubfield) == $itemnumber) { + # We save it + $item = $fielditem; + # Then delete it from the record + $record->delete_field($fielditem) + } + } + + # If we found an item (should always true, except in case of database-marcxml inconsistency) + if ($item) { + + # Checking if the item we want to move is in an order + my $order = GetOrderFromItemnumber($itemnumber); + if ($order) { + # Replacing the biblionumber within the order if necessary + $order->{'biblionumber'} = $tobiblio; + ModOrder($order); + } + + # Saving the modification + ModBiblioMarc($record, $frombiblio, $frameworkcode); + + # Getting the record we want to move the item to + $record = GetMarcBiblio($tobiblio); + + # Inserting the previously saved item + $record->insert_fields_ordered($item); + + # Saving the modification + ModBiblioMarc($record, $tobiblio, $frameworkcode); + + } else { + return undef; + } + } else { + return undef; + } +} + +=head2 DelItemCheck + + DelItemCheck($dbh, $biblionumber, $itemnumber); + +Exported function (core API) for deleting an item record in Koha if there no current issue. + +=cut + +sub DelItemCheck { + my ( $dbh, $biblionumber, $itemnumber ) = @_; + my $error; + + # check that there is no issue on this item before deletion. + my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?"); + $sth->execute($itemnumber); + + my $item = GetItem($itemnumber); + my $onloan = $sth->fetchrow; + if ($onloan) { + $error = "book_on_loan"; + } + elsif (C4::Context->preference("IndependantBranches") and (C4::Context->userenv->{branch} ne $item->{C4::Context->preference("HomeOrHoldingBranch")||'homebranch'})){ + $error = "not_same_branch"; + } + else { + if ($onloan){ + $error = "book_on_loan" + } + else { + # check it doesnt have a waiting reserve + $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?"); + $sth->execute($itemnumber); + my $reserve=$sth->fetchrow; + if ($reserve) { + $error = "book_reserved"; + } + else { + DelItem($dbh, $biblionumber, $itemnumber); + return 1; + } + } + } + return $error; +} + +=head2 _koha_modify_item + + my ($itemnumber,$error) =_koha_modify_item( $item ); Perform the actual update of the C row. Note that this routine accepts a hashref specifying the columns to update. @@ -1872,17 +2159,12 @@ sub _koha_modify_item { $error.="ERROR in _koha_modify_item $query".$dbh->errstr; warn $error; } - $sth->finish(); return ($item->{'itemnumber'},$error); } =head2 _koha_delete_item -=over 4 - -_koha_delete_item( $dbh, $itemnum ); - -=back + _koha_delete_item( $dbh, $itemnum ); Internal function to delete an item record from the koha tables @@ -1895,7 +2177,6 @@ sub _koha_delete_item { my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?"); $sth->execute($itemnum); my $data = $sth->fetchrow_hashref(); - $sth->finish(); my $query = "INSERT INTO deleteditems SET "; my @bind = (); foreach my $key ( keys %$data ) { @@ -1905,22 +2186,16 @@ sub _koha_delete_item { $query =~ s/\,$//; $sth = $dbh->prepare($query); $sth->execute(@bind); - $sth->finish(); # delete from items table $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?"); $sth->execute($itemnum); - $sth->finish(); return undef; } =head2 _marc_from_item_hash -=over 4 - -my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]); - -=back + my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]); Given an item hash representing a complete item record, create a C object containing an embedded @@ -1948,17 +2223,20 @@ sub _marc_from_item_hash { : () } keys %{ $item } }; my $item_marc = MARC::Record->new(); - foreach my $item_field (keys %{ $mungeditem }) { - my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode); - next unless defined $tag and defined $subfield; # skip if not mapped to MARC field - if (my $field = $item_marc->field($tag)) { - $field->add_subfields($subfield => $mungeditem->{$item_field}); - } else { - my $add_subfields = []; - if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) { - $add_subfields = $unlinked_item_subfields; + foreach my $item_field ( keys %{$mungeditem} ) { + my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode ); + next unless defined $tag and defined $subfield; # skip if not mapped to MARC field + my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1); + foreach my $value (@values){ + if ( my $field = $item_marc->field($tag) ) { + $field->add_subfields( $subfield => $value ); + } else { + my $add_subfields = []; + if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) { + $add_subfields = $unlinked_item_subfields; + } + $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields ); } - $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field}, @$add_subfields); } } @@ -1967,11 +2245,7 @@ sub _marc_from_item_hash { =head2 _add_item_field_to_biblio -=over 4 - -_add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode); - -=back + _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode); Adds the fields from a MARC record containing the representation of a Koha item record to the MARC @@ -1994,11 +2268,7 @@ sub _add_item_field_to_biblio { =head2 _replace_item_field_in_biblio -=over - -&_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode) - -=back + &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode) Given a MARC::Record C<$item_marc> containing one tag with the MARC representation of the item, examine the biblio MARC @@ -2063,11 +2333,7 @@ sub _repack_item_errors { =head2 _get_unlinked_item_subfields -=over 4 - -my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode); - -=back + my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode); =cut @@ -2097,11 +2363,7 @@ sub _get_unlinked_item_subfields { =head2 _get_unlinked_subfields_xml -=over 4 - -my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields); - -=back + my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields); =cut @@ -2123,11 +2385,7 @@ sub _get_unlinked_subfields_xml { =head2 _parse_unlinked_item_subfields_from_xml -=over 4 - -my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}): - -=back + my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}): =cut