Bug 30003: Prevent double up of point-of-sale items
[koha-ffzg.git] / C4 / Items.pm
index aecf96a..6fe6768 100644 (file)
@@ -20,12 +20,12 @@ package C4::Items;
 
 use Modern::Perl;
 
-use vars qw(@ISA @EXPORT);
+our (@ISA, @EXPORT_OK);
 BEGIN {
     require Exporter;
     @ISA = qw(Exporter);
 
-    @EXPORT = qw(
+    @EXPORT_OK = qw(
         AddItemFromMarc
         AddItemBatchFromMarc
         ModItemFromMarc
@@ -39,32 +39,29 @@ BEGIN {
         GetHostItemsInfo
         get_hostitemnumbers_of
         GetHiddenItemnumbers
-        MoveItemFromBiblio
+        GetMarcItem
         CartToShelf
         GetAnalyticsCount
         SearchItems
         PrepareItemrecordDisplay
+        ToggleNewStatus
     );
 }
 
-use Carp;
-use Try::Tiny;
-use Encode;
+use Carp qw( croak );
 use C4::Context;
 use C4::Koha;
-use C4::Biblio;
-use Koha::DateUtils;
+use C4::Biblio qw( GetMarcStructure TransformMarcToKoha );
+use Koha::DateUtils qw( dt_from_string output_pref );
 use MARC::Record;
-use C4::ClassSource;
-use C4::Log;
-use List::MoreUtils qw(any);
-use YAML::XS;
+use C4::ClassSource qw( GetClassSort GetClassSources GetClassSource );
+use C4::Log qw( logaction );
+use List::MoreUtils qw( any );
 use DateTime::Format::MySQL;
-use Data::Dumper; # used as part of logging item record changes, not just for
                   # debugging; so please don't remove this
 
 use Koha::AuthorisedValues;
-use Koha::DateUtils qw(dt_from_string);
+use Koha::DateUtils qw( dt_from_string output_pref );
 use Koha::Database;
 
 use Koha::Biblioitems;
@@ -306,6 +303,9 @@ sub ModItemFromMarc {
     my $item_object = Koha::Items->find($itemnumber);
     my $item = TransformMarcToKoha( $localitemmarc, $frameworkcode, 'items' );
 
+    my ( $perm_loc_tag, $perm_loc_subfield ) = C4::Biblio::GetMarcFromKohaField( "items.permanent_location" );
+    my $has_permanent_location = defined $perm_loc_tag && defined $item_marc->subfield( $perm_loc_tag, $perm_loc_subfield );
+
     # Retrieving the values for the fields that are not linked
     my @mapped_fields = Koha::MarcSubfieldStructures->search(
         {
@@ -328,6 +328,8 @@ sub ModItemFromMarc {
     $item_object = $item_object->set_or_blank($item);
     $item_object->cn_sort($existing_cn_sort); # Resetting to the existing value
 
+    $item_object->make_column_dirty('permanent_location') if $has_permanent_location;
+
     my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
     $item_object->more_subfields_xml(_get_unlinked_subfields_xml($unlinked_item_subfields));
     $item_object->store({ skip_record_index => $params->{skip_record_index} });
@@ -634,7 +636,7 @@ sub GetItemsForInventory {
             '+select' => [ 'marc_subfield_structures.kohafield', 'marc_subfield_structures.frameworkcode', 'me.authorised_value', 'me.lib' ],
             '+as'     => [ 'kohafield',                          'frameworkcode',                          'authorised_value',    'lib' ],
         }
-    );
+    )->as_list;
 
     my $avmapping = { map { $_->get_column('kohafield') . ',' . $_->get_column('frameworkcode') . ',' . $_->get_column('authorised_value') => $_->get_column('lib') } @avs };
 
@@ -805,7 +807,7 @@ sub GetItemsInfo {
     }
 
     return $serial
-        ? sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results
+        ? sort { ($b->{'publisheddate'} || $b->{'enumchron'} || "") cmp ($a->{'publisheddate'} || $a->{'enumchron'} || "") } @results
         : @results;
 }
 
@@ -892,8 +894,7 @@ sub GetHostItemsInfo {
     }
 
     my @fields;
-    if( C4::Context->preference('marcflavour') eq 'MARC21' ||
-      C4::Context->preference('marcflavour') eq 'NORMARC') {
+    if( C4::Context->preference('marcflavour') eq 'MARC21' ) {
         @fields = $record->field('773');
     } elsif( C4::Context->preference('marcflavour') eq 'UNIMARC') {
         @fields = $record->field('461');
@@ -938,7 +939,7 @@ sub get_hostitemnumbers_of {
     my ( @returnhostitemnumbers, $tag, $biblio_s, $item_s );
 
     my $marcflavor = C4::Context->preference('marcflavour');
-    if ( $marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC' ) {
+    if ( $marcflavor eq 'MARC21' ) {
         $tag      = '773';
         $biblio_s = '0';
         $item_s   = '9';
@@ -988,17 +989,11 @@ sub GetHiddenItemnumbers {
     }
     my @resultitems;
 
-    my $yaml = C4::Context->preference('OpacHiddenItems');
-    return () if (! $yaml =~ /\S/ );
-    $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
-    my $hidingrules;
-    eval {
-        $hidingrules = YAML::XS::Load(Encode::encode_utf8($yaml));
-    };
-    if ($@) {
-        warn "Unable to parse OpacHiddenItems syspref : $@";
-        return ();
-    }
+    my $hidingrules = C4::Context->yaml_preference('OpacHiddenItems');
+
+    return
+        unless $hidingrules;
+
     my $dbh = C4::Context->dbh;
 
     # For each item
@@ -1104,57 +1099,6 @@ the inner workings of C<C4::Items>.
 
 =cut
 
-=head2 MoveItemFromBiblio
-
-  MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
-
-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 ( $tobiblioitem ) = $dbh->selectrow_array(q|
-        SELECT biblioitemnumber
-        FROM biblioitems
-        WHERE biblionumber = ?
-    |, undef, $tobiblio );
-    my $return = $dbh->do(q|
-        UPDATE items
-        SET biblioitemnumber = ?,
-            biblionumber = ?
-        WHERE itemnumber = ?
-            AND biblionumber = ?
-    |, undef, $tobiblioitem, $tobiblio, $itemnumber, $frombiblio );
-    if ($return == 1) {
-        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
-        $indexer->index_records( $tobiblio, "specialUpdate", "biblioserver" );
-        $indexer->index_records( $frombiblio, "specialUpdate", "biblioserver" );
-           # Checking if the item we want to move is in an order 
-        require C4::Acquisition;
-        my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
-           if ($order) {
-                   # Replacing the biblionumber within the order if necessary
-                   $order->{'biblionumber'} = $tobiblio;
-               C4::Acquisition::ModOrder($order);
-           }
-
-        # Update reserves, hold_fill_targets, tmp_holdsqueue and linktracker tables
-        for my $table_name ( qw( reserves hold_fill_targets tmp_holdsqueue linktracker ) ) {
-            $dbh->do( qq|
-                UPDATE $table_name
-                SET biblionumber = ?
-                WHERE itemnumber = ?
-            |, undef, $tobiblio, $itemnumber );
-        }
-        return $tobiblio;
-       }
-    return;
-}
-
 =head2 _marc_from_item_hash
 
   my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
@@ -1350,7 +1294,8 @@ sub _SearchItems_build_where_fragment {
         my @columns = Koha::Database->new()->schema()->resultset('Item')->result_source->columns;
         push @columns, Koha::Database->new()->schema()->resultset('Biblio')->result_source->columns;
         push @columns, Koha::Database->new()->schema()->resultset('Biblioitem')->result_source->columns;
-        my @operators = qw(= != > < >= <= like);
+        push @columns, Koha::Database->new()->schema()->resultset('Issue')->result_source->columns;
+        my @operators = qw(= != > < >= <= is like);
         push @operators, 'not like';
         my $field = $filter->{field} // q{};
         if ( (0 < grep { $_ eq $field } @columns) or (substr($field, 0, 5) eq 'marc:') ) {
@@ -1411,6 +1356,11 @@ sub _SearchItems_build_where_fragment {
                     str => "$column $op (" . join (',', ('?') x @$query) . ")",
                     args => $query,
                 };
+            } elsif ( $op eq 'is' ) {
+                $where_fragment = {
+                    str => "$column $op $query",
+                    args => [],
+                };
             } else {
                 $where_fragment = {
                     str => "$column $op ?",
@@ -1443,7 +1393,7 @@ A filter has the following keys:
 
 =item * query: the value to search in this column
 
-=item * operator: comparison operator. Can be one of = != > < >= <= like 'not like'
+=item * operator: comparison operator. Can be one of = != > < >= <= like 'not like' is
 
 =back
 
@@ -1502,6 +1452,7 @@ sub SearchItems {
           LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
           LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
           LEFT JOIN biblio_metadata ON biblio_metadata.biblionumber = biblio.biblionumber
+          LEFT JOIN issues ON issues.itemnumber = items.itemnumber
           WHERE 1
     };
     if (defined $where_str and $where_str ne '') {
@@ -1514,10 +1465,17 @@ sub SearchItems {
     my @columns = Koha::Database->new()->schema()->resultset('Item')->result_source->columns;
     push @columns, Koha::Database->new()->schema()->resultset('Biblio')->result_source->columns;
     push @columns, Koha::Database->new()->schema()->resultset('Biblioitem')->result_source->columns;
-    my $sortby = (0 < grep {$params->{sortby} eq $_} @columns)
-        ? $params->{sortby} : 'itemnumber';
-    my $sortorder = (uc($params->{sortorder}) eq 'ASC') ? 'ASC' : 'DESC';
-    $query .= qq{ ORDER BY $sortby $sortorder };
+    push @columns, Koha::Database->new()->schema()->resultset('Issue')->result_source->columns;
+
+    if ( $params->{sortby} eq 'availability' ) {
+        my $sortorder = (uc($params->{sortorder}) eq 'ASC') ? 'ASC' : 'DESC';
+        $query .= qq{ ORDER BY onloan $sortorder };
+    } else {
+        my $sortby = (0 < grep {$params->{sortby} eq $_} @columns)
+            ? $params->{sortby} : 'itemnumber';
+        my $sortorder = (uc($params->{sortorder}) eq 'ASC') ? 'ASC' : 'DESC';
+        $query .= qq{ ORDER BY $sortby $sortorder };
+    }
 
     my $rows = $params->{rows};
     my @limit_args;
@@ -1581,10 +1539,12 @@ sub _find_value {
 
 =head2 PrepareItemrecordDisplay
 
-  PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
+  PrepareItemrecordDisplay($bibnum,$itemumber,$defaultvalues,$frameworkcode);
 
 Returns a hash with all the fields for Display a given item data in a template
 
+$defaultvalues should either contain a hashref of values for the new item, or be undefined.
+
 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
 
 =cut
@@ -1692,6 +1652,7 @@ sub PrepareItemrecordDisplay {
                         my $CNtag      = substr( $itemcn_pref, 0, 3 );
                         next unless my $field = $itemrecord->field($CNtag);
                         my $CNsubfields = substr( $itemcn_pref, 3 );
+                        $CNsubfields = undef if $CNsubfields eq '';
                         $defaultvalue = $field->as_string( $CNsubfields, ' ');
                         last if $defaultvalue;
                     }
@@ -1812,7 +1773,7 @@ sub PrepareItemrecordDisplay {
                         name => $subfield->{value_builder},
                         item_style => 1,
                     });
-                    my $pars = { dbh => $dbh, record => undef, tagslib =>$tagslib, id => $subfield_data{id}, tabloop => undef };
+                    my $pars = { dbh => $dbh, record => undef, tagslib =>$tagslib, id => $subfield_data{id} };
                     $plugin->build( $pars );
                     if ( $itemrecord and my $field = $itemrecord->field($tag) ) {
                         $defaultvalue = $field->subfield($subfield->{subfield}) || q{};