AR.pm: SL::TransNumber anstelle von Form->update_defaults für Nummernkreise nutzen
[kivitendo-erp.git] / SL / Helper / Csv.pm
index c5d2f4a..2730502 100644 (file)
@@ -111,13 +111,31 @@ sub _check_multiplexed {
     if (scalar @profile > 1) {
       # Each profile needs a class and a row_ident
       my $info_ok = all { defined $_->{class} && defined $_->{row_ident} } @profile;
+      $self->_push_error([
+        0,
+        "missing class or row_ident in one of the profiles for multiplexed data",
+        0,
+        0]) unless $info_ok;
 
       # If header is given, there need to be a header for each profile
       # and no empty headers.
       if ($info_ok && $self->header) {
         my @header = @{ $self->header };
-        $info_ok = $info_ok && scalar @profile == scalar @header;
-        $info_ok = $info_ok && all { scalar @$_ > 0} @header;
+        my $t_ok = scalar @profile == scalar @header;
+        $self->_push_error([
+          0,
+          "number of headers and number of profiles must be the same for multiplexed data",
+          0,
+          0]) unless $t_ok;
+        $info_ok = $info_ok && $t_ok;
+
+        $t_ok = all { scalar @$_ > 0} @header;
+        $self->_push_error([
+          0,
+          "no empty headers are allowed for multiplexed data",
+          0,
+          0]) unless $t_ok;
+        $info_ok = $info_ok && $t_ok;
       }
       $self->is_multiplexed($info_ok);
       return $info_ok;
@@ -144,7 +162,11 @@ sub _check_header {
         0,
       ]) unless $h;
 
-      push @{ $header }, $h;
+      if ($self->is_multiplexed) {
+        push @{ $header }, $h;
+      } else {
+        $header = $h;
+      }
     }
   }
 
@@ -153,14 +175,19 @@ sub _check_header {
   # data with a discouraged but valid byte order mark
   # if not removed, the first header field will not be recognized
   if ($header) {
-    my $h = $header->[0];
+    my $h = ($self->is_multiplexed)? $header->[0] : $header;
+
     if ($h && $h->[0] && $self->encoding =~ /utf-?8/i) {
       $h->[0] =~ s/^\x{FEFF}//;
     }
   }
 
   # check, if all header fields are parsed well
-  return unless $header && all { $_ } @$header;
+  if ($self->is_multiplexed) {
+    return unless $header && all { $_ } @$header;
+  } else {
+    return unless $header;
+  }
 
   # Special case: human stupidity
   # people insist that case sensitivity doesn't exist and try to enter all
@@ -170,8 +197,9 @@ sub _check_header {
   if ($self->case_insensitive_header) {
     die 'case_insensitive_header is only possible with profile' unless $self->profile;
     if ($header) {
-      my $p_num = 0;
-      foreach my $h (@{ $header }) {
+      my $h_aref = ($self->is_multiplexed)? $header : [ $header ];
+      my $p_num  = 0;
+      foreach my $h (@{ $h_aref }) {
         my @names = (
           keys %{ $self->profile->[$p_num]->{profile} || {} },
         );
@@ -229,13 +257,13 @@ sub _header_by_row {
 
   # initialize lookup hash if not already done
   if ($self->is_multiplexed && ! defined $self->_row_header ) {
-    $self->_row_header({ pairwise { $a->{row_ident} => $b } @{ $self->profile }, @{ $self->header } });
+    $self->_row_header({ pairwise { no warnings 'once'; $a->{row_ident} => $b } @{ $self->profile }, @{ $self->header } });
   }
 
   if ($self->is_multiplexed) {
     return $self->_row_header->{$row->[0]}
   } else {
-    return $self->header->[0];
+    return $self->header;
   }
 }
 
@@ -304,7 +332,7 @@ SL::Helper::Csv - take care of csv file uploads
     sep_char    => ',',     # default ';'
     quote_char  => '\'',    # default '"'
     escape_char => '"',     # default '"'
-    header      => [ [qw(id text sellprice word)] ], # see later
+    header      => [ qw(id text sellprice word) ], # see later
     profile     => [ { profile => { sellprice => 'sellprice_as_number'},
                        class   => 'SL::DB::Part' } ],
   );
@@ -409,21 +437,26 @@ Same as in L<Text::CSV>
 
 =item C<header> \@HEADERS
 
-If given, it contains an ARRAYREF for each different class type (i.e. one
-ARRAYREF if the data is only of one class type). These ARRAYREFS are the header
-fields which are an array of columns. In this case the first lines are not used
-as a header. Empty header fields will be ignored in objects.
+If given, it contains an ARRAY of the header fields for not multiplexed data.
+Or an ARRAYREF for each different class type for multiplexed data. These
+ARRAYREFS are the header fields which are an array of columns. In this case
+the first lines are not used as a header. Empty header fields will be ignored
+in objects.
 
 If not given, headers are taken from the first n lines of data, where n is the
 number of different class types.
 
+In case of multiplexed data the first column must be named 'datatype'. This
+name must be given in the header.
+
 Examples:
 
   classic data of one type:
-  [ [ 'name', 'street', 'zipcode', 'city' ] ]
+  [ 'name', 'street', 'zipcode', 'city' ]
 
   multiplexed data with two different types
-  [ [ 'ordernumber', 'customer', 'transdate' ], [ 'partnumber', 'qty', 'sellprice' ] ]
+  [ [ 'datatype', 'ordernumber', 'customer', 'transdate' ],
+    [ 'datatype', 'partnumber', 'qty', 'sellprice' ] ]
 
 =item C<profile> [{profile => \%ACCESSORS, class => class, row_ident => ri},]
 
@@ -464,7 +497,8 @@ If C<class> is present, the line will be handed to the new sub of this class,
 and the return value used instead of the line itself.
 
 C<row_ident> is a string to recognize the right profile and class for each data
-line in multiplexed data.
+line in multiplexed data. It must match the value in the column 'dataype' for
+each class.
 
 In case of multiplexed data, C<class> and C<row_ident> must be given.
 Example: