X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;ds=sidebyside;f=C4%2FItems.pm;h=3a4ec9173ca834f063a1e3d25099547601f49ad6;hb=885c68e269419813dafde37fbeb32052e16974a1;hp=427e21f08a8370127f2372fd35aeaa74830f7be3;hpb=288b74cf3a5accace7e85b59ac54324242bf7bf0;p=koha.git diff --git a/C4/Items.pm b/C4/Items.pm index 427e21f08a..3a4ec9173c 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -19,6 +19,7 @@ package C4::Items; use strict; +use Carp; use C4::Context; use C4::Koha; use C4::Biblio; @@ -29,6 +30,7 @@ use C4::Log; use C4::Branch; require C4::Reserves; use C4::Charset; +use C4::Acquisition; use vars qw($VERSION @ISA @EXPORT); @@ -45,6 +47,7 @@ BEGIN { AddItem AddItemBatchFromMarc ModItemFromMarc + Item2Marc ModItem ModDateLastSeen ModItemTransfer @@ -62,6 +65,11 @@ BEGIN { GetItemsInfo get_itemnumbers_of GetItemnumberFromBarcode + + DelItemCheck + MoveItemFromBiblio + GetLatestAcquisitions + CartToShelf ); } @@ -153,6 +161,34 @@ sub GetItem { return $data; } # sub GetItem +=head2 CartToShelf + +=over 4 + +CartToShelf($itemnumber); + +=back + +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. + +=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 =over 4 @@ -224,6 +260,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; @@ -356,8 +398,49 @@ 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, + uri => undef, + wthdrawn => 0, +); + sub ModItemFromMarc { my $item_marc = shift; my $biblionumber = shift; @@ -365,7 +448,14 @@ sub ModItemFromMarc { my $dbh = C4::Context->dbh; my $frameworkcode = GetFrameworkCode( $biblionumber ); - my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode ); + my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode); + + my $localitemmarc=MARC::Record->new; + $localitemmarc->append_fields($item_marc->field($itemtag)); + my $item = &TransformMarcToKoha( $dbh, $item_marc, $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($item_marc, $frameworkcode); return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields); @@ -719,7 +809,6 @@ sub GetItemStatus { while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { $itemstatus{$authorisedvalue} = $lib; } - $authvalsth->finish; return \%itemstatus; exit 1; } @@ -728,7 +817,6 @@ sub GetItemStatus { #No authvalue list # build default } - $sth->finish; } #No authvalue list @@ -819,7 +907,6 @@ sub GetItemLocation { while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) { $itemlocation{$authorisedvalue} = $lib; } - $authvalsth->finish; return \%itemlocation; exit 1; } @@ -828,7 +915,6 @@ sub GetItemLocation { #No authvalue list # build default } - $sth->finish; } #No authvalue list @@ -841,28 +927,35 @@ sub GetItemLocation { =over 4 -$items = GetLostItems($where,$orderby); +$items = GetLostItems( $where, $orderby ); =back -This function get the items lost into C<$items>. +This function gets a list of lost items. =over 2 =item input: + C<$where> is a hashref. it containts a field of the items table as key -and the value to match as value. -C<$orderby> is a field of the items table. +and the value to match as value. For example: + +{ barcode => 'abc123', + homebranch => 'CPL', } + +C<$orderby> is a field of the items table by which the resultset +should be orderd. =item return: -C<$items> is a reference to an array full of hasref which keys are items' table column. + +C<$items> is a reference to an array full of hashrefs with columns +from the "items" table as keys. =item usage in the perl script: -my %where; -$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 @@ -876,84 +969,120 @@ 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 '%" . $where->{$key} . "%'"; + $query .= " AND $key LIKE ?"; + push @query_parameters, "%$where->{$key}%"; + } + my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/; + + if ( defined $orderby && grep($orderby, @ordervalues)) { + $query .= ' ORDER BY '.$orderby; } - $query .= " ORDER BY ".$orderby." " if defined $orderby; my $sth = $dbh->prepare($query); - $sth->execute; - my @items; + $sth->execute( @query_parameters ); + my $items = []; while ( my $row = $sth->fetchrow_hashref ){ - push @items, $row; + push @$items, $row; } - return \@items; + return $items; } =head2 GetItemsForInventory =over 4 -$itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size) +$itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size, $statushash); =back Retrieve a list of title/authors/barcode/callnumber, for biblio inventory. -The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber. -It is ordered by callnumber,title. +The sub returns a reference to a list of hashes, each containing +itemnumber, author, title, barcode, item callnumber, and date last +seen. It is ordered by callnumber then title. -The minlocation & maxlocation parameters are used to specify a range of item callnumbers +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, $branch, $offset, $size, $statushash ) = @_; my $dbh = C4::Context->dbh; - my $sth; + my ( @bind_params, @where_strings ); + + my $query = <<'END_SQL'; +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 +END_SQL + 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); - my $query = - "SELECT 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 <=? - AND (datelastseen< ? OR datelastseen IS NULL)"; - $query.= " AND items.location=".$dbh->quote($location) if $location; - $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch; - $query.= " AND biblioitems.itemtype=".$dbh->quote($itemtype) if $itemtype; - $query .= " ORDER BY itemcallnumber,title"; - $sth = $dbh->prepare($query); - $sth->execute( $minlocation, $maxlocation, $datelastseen ); + $datelastseen = format_date_in_iso($datelastseen); + push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)'; + push @bind_params, $datelastseen; } - else { - my $query =" - SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen - FROM items - LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber - LEFT JOIN biblioitems on items.biblionumber=biblioitems.biblionumber - WHERE itemcallnumber>= ? - AND itemcallnumber <=?"; - $query.= " AND items.location=".$dbh->quote($location) if $location; - $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch; - $query.= " AND biblioitems.itemtype=".$dbh->quote($itemtype) if $itemtype; - $query .= " ORDER BY itemcallnumber,title"; - $sth = $dbh->prepare($query); - $sth->execute( $minlocation, $maxlocation ); + + if ( $location ) { + push @where_strings, 'items.location = ?'; + push @bind_params, $location; + } + + if ( $branch ) { + push @where_strings, 'items.homebranch = ?'; + push @bind_params, $branch; + } + + if ( $itemtype ) { + push @where_strings, 'biblioitems.itemtype = ?'; + push @bind_params, $itemtype; + } + + 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 ); + my @results; $size--; while ( my $row = $sth->fetchrow_hashref ) { @@ -987,7 +1116,6 @@ sub GetItemsCount { my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my $count = $sth->fetchrow; - $sth->finish; return ($count); } @@ -1050,7 +1178,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 @@ -1064,10 +1191,8 @@ sub GetItemsByBiblioitemnumber { $data->{"borrower$i2"} = $data2->{'borrowernumber'}; $i2++; } - $sth2->finish; push(@results,$data); } - $sth->finish; return (\@results); } @@ -1124,19 +1249,35 @@ 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 + 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 @@ -1146,6 +1287,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}; @@ -1155,7 +1297,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}); } } @@ -1168,12 +1310,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 = ? @@ -1207,6 +1348,7 @@ sub GetItemsInfo { my ($lib) = $sthnflstatus->fetchrow; $data->{notforloanvalue} = $lib; } + $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes')); # my stack procedures my $stackstatus = $dbh->prepare( @@ -1234,6 +1376,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; @@ -1247,14 +1390,67 @@ sub GetItemsInfo { $results[$i] = $data; $i++; } - $sth->finish; if($serial) { - return( sort { $b->{'publisheddate'} cmp $a->{'publisheddate'} } @results ); + return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results ); } else { return (@results); } } +=head2 GetLastAcquisitions + +=over 4 + +my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 'itemtypes' => ('BK','BD')}, 10); + +=back + +=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 =over 4 @@ -1315,6 +1511,106 @@ sub GetItemnumberFromBarcode { return ($result); } +=head3 get_item_authorised_values + + find the types and values for all authorised values assigned to this item. + + parameters: + itemnumber + + returns: a hashref malling the authorised value to the value set for this itemnumber + + $authorised_values = { + 'CCODE' => undef, + 'DAMAGED' => '0', + 'LOC' => '3', + 'LOST' => '0' + 'NOT_LOAN' => '0', + 'RESTRICTED' => undef, + 'STACK' => undef, + 'WITHDRAWN' => '0', + 'branches' => 'CPL', + 'cn_source' => undef, + 'itemtypes' => 'SER', + }; + + Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level. + +=cut + +sub get_item_authorised_values { + my $itemnumber = shift; + + # assume that these entries in the authorised_value table are item level. + my $query = q(SELECT distinct authorised_value, kohafield + FROM marc_subfield_structure + WHERE kohafield like 'item%' + AND authorised_value != '' ); + + my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' ); + my $iteminfo = GetItem( $itemnumber ); + # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) ); + my $return; + foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) { + my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'}; + $field =~ s/^items\.//; + if ( exists $iteminfo->{ $field } ) { + $return->{ $this_authorised_value } = $iteminfo->{ $field }; + } + } + # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) ); + return $return; +} + +=head3 get_authorised_value_images + + 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_authorised_values or + from C4::Biblio::get_biblio_authorised_values + + 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. + +=cut + +sub get_authorised_value_images { + my $authorised_values = shift; + + my @imagelist; + + my $authorised_value_list = GetAuthorisedValues(); + # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) ); + foreach my $this_authorised_value ( @$authorised_value_list ) { + if ( exists $authorised_values->{ $this_authorised_value->{'category'} } + && $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::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ), + label => $this_authorised_value->{'lib'}, + category => $this_authorised_value->{'category'}, + value => $this_authorised_value->{'authorised_value'}, }; + } + } + } + + # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) ); + return \@imagelist; + +} + =head1 LIMITED USE FUNCTIONS The following functions, while part of the public API, @@ -1359,19 +1655,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 @@ -1522,6 +1827,9 @@ 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'}; + } } =head2 _get_single_item_column @@ -1608,20 +1916,8 @@ 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 @@ -1673,9 +1969,10 @@ sub _koha_new_item { ccode = ?, itype = ?, materials = ?, - uri = ?, + uri = ?, enumchron = ?, - more_subfields_xml = ? + more_subfields_xml = ?, + copynumber = ? "; my $sth = $dbh->prepare($query); $sth->execute( @@ -1710,16 +2007,131 @@ sub _koha_new_item { $item->{'materials'}, $item->{'uri'}, $item->{'enumchron'}, - $item->{'more_subfields_xml'}, + $item->{'more_subfields_xml'}, + $item->{'copynumber'}, ); my $itemnumber = $dbh->{'mysql_insertid'}; if ( defined $sth->errstr ) { $error.="ERROR in _koha_new_item $query".$sth->errstr; } - $sth->finish(); return ( $itemnumber, $error ); } +=head2 MoveItemFromBiblio + +=over 4 + +MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio); + +=back + +Moves an item from a biblio to another + +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 + +=over 4 + +DelItemCheck($dbh, $biblionumber, $itemnumber); + +=back + +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 $onloan=$sth->fetchrow; + + if ($onloan){ + $error = "book_on_loan" + }else{ + # check it doesnt have a waiting reserve + $sth=$dbh->prepare("SELECT * FROM reserves WHERE found = 'W' 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 =over 4 @@ -1753,7 +2165,6 @@ sub _koha_modify_item { $error.="ERROR in _koha_modify_item $query".$dbh->errstr; warn $error; } - $sth->finish(); return ($item->{'itemnumber'},$error); } @@ -1776,7 +2187,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 ) { @@ -1786,12 +2196,10 @@ 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; } @@ -1866,7 +2274,6 @@ sub _add_item_field_to_biblio { my ($item_marc, $biblionumber, $frameworkcode) = @_; my $biblio_marc = GetMarcBiblio($biblionumber); - foreach my $field ($item_marc->fields()) { $biblio_marc->append_fields($field); }