Merge remote branch 'kc/new/bug_4885' into kcmaster
[koha_gimpoz] / C4 / Items.pm
index 825667f..05df0c5 100644 (file)
@@ -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
@@ -62,6 +67,12 @@ BEGIN {
         GetItemsInfo
         get_itemnumbers_of
         GetItemnumberFromBarcode
+        GetBarcodeFromItemnumber
+
+               DelItemCheck
+               MoveItemFromBiblio 
+               GetLatestAcquisitions
+        CartToShelf
     );
 }
 
@@ -108,11 +119,7 @@ of C<C4::Items>
 
 =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 +149,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 +159,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 +199,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 +250,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 +270,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 +289,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 +374,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<MARC::Record> object containing an embedded item field.
 This API is meant for the use of C<additem.pl>; for 
 other purposes, C<ModItem> 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 +519,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 +544,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 +560,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 +597,6 @@ sub DelItem {
 
 =head2 CheckItemPreSave
 
-=over 4
-
     my $item_ref = TransformMarcToKoha($marc, 'items');
     # do stuff
     my %errors = CheckItemPreSave($item_ref);
@@ -553,8 +610,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 +692,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<items.notforloan> field.
@@ -656,32 +707,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
-
-<select name="statusloop">
-    <option value="">Default</option>
-<!-- TMPL_LOOP name="statusloop" -->
-    <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-
-=back
+ <select name="statusloop">
+     <option value="">Default</option>
+ <!-- TMPL_LOOP name="statusloop" -->
+     <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
+ <!-- /TMPL_LOOP -->
+ </select>
 
 =cut
 
@@ -719,7 +762,6 @@ sub GetItemStatus {
             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
                 $itemstatus{$authorisedvalue} = $lib;
             }
-            $authvalsth->finish;
             return \%itemstatus;
             exit 1;
         }
@@ -728,7 +770,6 @@ sub GetItemStatus {
             #No authvalue list
             # build default
         }
-        $sth->finish;
     }
 
     #No authvalue list
@@ -739,11 +780,7 @@ sub GetItemStatus {
 
 =head2 GetItemLocation
 
-=over 4
-
-$itemlochash = GetItemLocation($fwk);
-
-=back
+  $itemlochash = GetItemLocation($fwk);
 
 Returns a list of valid values for the
 C<items.location> field.
@@ -756,34 +793,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
-
-<select name="location">
-    <option value="">Default</option>
-<!-- TMPL_LOOP name="itemlocationloop" -->
-    <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-
-=back
+  <select name="location">
+      <option value="">Default</option>
+  <!-- TMPL_LOOP name="itemlocationloop" -->
+      <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
+  <!-- /TMPL_LOOP -->
+  </select>
 
 =cut
 
@@ -819,7 +848,6 @@ sub GetItemLocation {
             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
                 $itemlocation{$authorisedvalue} = $lib;
             }
-            $authvalsth->finish;
             return \%itemlocation;
             exit 1;
         }
@@ -828,7 +856,6 @@ sub GetItemLocation {
             #No authvalue list
             # build default
         }
-        $sth->finish;
     }
 
     #No authvalue list
@@ -839,30 +866,33 @@ sub GetItemLocation {
 
 =head2 GetLostItems
 
-=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 +906,122 @@ 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)
-
-=back
+  $itemlist = GetItemsForInventory($minlocation, $maxlocation, 
+                 $location, $itemtype $datelastseen, $branch, 
+                 $offset, $size, $statushash);
 
 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, $branchcode, $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 ( $branchcode ) {
+        if($branch eq "homebranch"){
+        push @where_strings, 'items.homebranch = ?';
+        }else{
+            push @where_strings, 'items.holdingbranch = ?';
+        }
+        push @bind_params, $branchcode;
+    }
+    
+    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 ) {
@@ -969,10 +1037,7 @@ sub GetItemsForInventory {
 
 =head2 GetItemsCount
 
-=over 4
-$count = &GetItemsCount( $biblionumber);
-
-=back
+  $count = &GetItemsCount( $biblionumber);
 
 This function return count of item with $biblionumber
 
@@ -987,17 +1052,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
 
@@ -1014,11 +1074,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<C4::XISBN>
@@ -1050,7 +1106,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,20 +1119,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.
 
@@ -1124,19 +1173,37 @@ If this is set, it is set to C<One Order>.
 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
@@ -1146,6 +1213,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 +1223,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 +1236,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 = ?
@@ -1208,6 +1275,31 @@ sub GetItemsInfo {
             $data->{notforloanvalue} = $lib;
         }
 
+        # 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(
             'SELECT authorised_value
@@ -1234,6 +1326,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,21 +1340,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 get_itemnumbers_of
+=head2 GetLastAcquisitions
 
-=over 4
+  my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 
+                                    'itemtypes' => ('BK','BD')}, 10);
 
-my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
+=cut
 
-=back
+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.
@@ -1296,11 +1435,7 @@ sub get_itemnumbers_of {
 
 =head2 GetItemnumberFromBarcode
 
-=over 4
-
-$result = GetItemnumberFromBarcode($barcode);
-
-=back
+  $result = GetItemnumberFromBarcode($barcode);
 
 =cut
 
@@ -1315,6 +1450,121 @@ 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.
+
+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,
@@ -1327,11 +1577,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<cataloguing/additem.pl>,
@@ -1359,19 +1605,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
@@ -1409,11 +1664,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
@@ -1437,11 +1688,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
@@ -1488,11 +1735,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.
@@ -1522,15 +1765,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<items>
 row specified by C<$itemnumber>.
@@ -1550,11 +1795,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<items.cn_sort>.
 
@@ -1569,11 +1810,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
@@ -1608,29 +1845,13 @@ C<items.wthdrawn>
 
 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<items> table.
 
@@ -1673,9 +1894,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,23 +1932,138 @@ 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 _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<items> row.  Note that this
 routine accepts a hashref specifying the columns to update.
@@ -1753,17 +2090,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
 
@@ -1776,7 +2108,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,22 +2117,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<MARC::Record> object containing an embedded
@@ -1829,17 +2154,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);
         }
     }
 
@@ -1848,11 +2176,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
@@ -1875,11 +2199,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
@@ -1944,11 +2264,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
 
@@ -1978,11 +2294,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
 
@@ -2004,11 +2316,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