Revision ccb40ac4
Von Bernd Bleßmann vor fast 11 Jahren hinzugefügt
SL/Helper/Csv.pm | ||
---|---|---|
7 | 7 |
use Carp; |
8 | 8 |
use IO::File; |
9 | 9 |
use Params::Validate qw(:all); |
10 |
use List::MoreUtils qw(all pairwise); |
|
10 |
use List::MoreUtils qw(all pairwise firstidx);
|
|
11 | 11 |
use Text::CSV_XS; |
12 | 12 |
use Rose::Object::MakeMethods::Generic scalar => [ qw( |
13 | 13 |
file encoding sep_char quote_char escape_char header profile |
14 | 14 |
numberformat dateformat ignore_unknown_columns strict_profile is_multiplexed |
15 | 15 |
_row_header _io _csv _objects _parsed _data _errors all_cvar_configs case_insensitive_header |
16 |
_multiplex_datatype_position |
|
16 | 17 |
) ]; |
17 | 18 |
|
18 | 19 |
use SL::Helper::Csv::Dispatcher; |
... | ... | |
59 | 60 |
$self->_open_file; |
60 | 61 |
return if ! $self->_check_multiplexed; |
61 | 62 |
return if ! $self->_check_header; |
63 |
return if ! $self->_check_multiplex_datatype_position; |
|
62 | 64 |
return if ! $self->dispatcher->parse_profile; |
63 | 65 |
return if ! $self->_parse_data; |
64 | 66 |
|
... | ... | |
216 | 218 |
return $self->header($header); |
217 | 219 |
} |
218 | 220 |
|
221 |
sub _check_multiplex_datatype_position { |
|
222 |
my ($self) = @_; |
|
223 |
|
|
224 |
return 1 if !$self->is_multiplexed; # ok if if not multiplexed |
|
225 |
|
|
226 |
my @positions = map { firstidx { 'datatype' eq lc($_) } @{ $_ } } @{ $self->header }; |
|
227 |
my $first_pos = $positions[0]; |
|
228 |
if (all { $first_pos == $_ } @positions) { |
|
229 |
$self->_multiplex_datatype_position($first_pos); |
|
230 |
return 1; |
|
231 |
} else { |
|
232 |
$self->_push_error([0, |
|
233 |
"datatype field must be at the same position for all datatypes for multiplexed data", |
|
234 |
0, |
|
235 |
0]); |
|
236 |
return 0; |
|
237 |
} |
|
238 |
} |
|
239 |
|
|
219 | 240 |
sub _parse_data { |
220 | 241 |
my ($self, %params) = @_; |
221 | 242 |
my (@data, @errors); |
... | ... | |
261 | 282 |
} |
262 | 283 |
|
263 | 284 |
if ($self->is_multiplexed) { |
264 |
return $self->_row_header->{$row->[0]}
|
|
285 |
return $self->_row_header->{$row->[$self->_multiplex_datatype_position]}
|
|
265 | 286 |
} else { |
266 | 287 |
return $self->header; |
267 | 288 |
} |
... | ... | |
380 | 401 |
This module can handle multiplexed data of different class types. In that case |
381 | 402 |
multiple profiles with classes and row identifiers must be given. Multiple |
382 | 403 |
headers may also be given or read from csv data. Data must contain the row |
383 |
identifier in the first column and it's field name must be 'datatype'.
|
|
404 |
identifier in the column named 'datatype'.
|
|
384 | 405 |
|
385 | 406 |
=back |
386 | 407 |
|
... | ... | |
446 | 467 |
If not given, headers are taken from the first n lines of data, where n is the |
447 | 468 |
number of different class types. |
448 | 469 |
|
449 |
In case of multiplexed data the first column must be named 'datatype'. This |
|
450 |
name must be given in the header. |
|
470 |
In case of multiplexed data there must be a column named 'datatype'. This |
|
471 |
column must be given in each header and must be at the same position in each |
|
472 |
header. |
|
451 | 473 |
|
452 | 474 |
Examples: |
453 | 475 |
|
454 | 476 |
classic data of one type: |
455 | 477 |
[ 'name', 'street', 'zipcode', 'city' ] |
456 | 478 |
|
457 |
multiplexed data with two different types |
|
479 |
multiplexed data with two different types:
|
|
458 | 480 |
[ [ 'datatype', 'ordernumber', 'customer', 'transdate' ], |
459 | 481 |
[ 'datatype', 'partnumber', 'qty', 'sellprice' ] ] |
460 | 482 |
|
locale/de/all | ||
---|---|---|
2116 | 2116 |
'The client has been created.' => 'Der Mandant wurde angelegt.', |
2117 | 2117 |
'The client has been deleted.' => 'Der Mandant wurde gelöscht.', |
2118 | 2118 |
'The client has been saved.' => 'Der Mandant wurde gespeichert.', |
2119 |
'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => 'Die Spalte "datatype" muss vorhanden sein und sie muss die erste Spalte sein. Die Werte in dieser Spalte müssen die Namen der Auftrag-/Positions-Zeilen (siehe Einstellungen) sein.',
|
|
2119 |
'The column "datatype" must be present and must be at the same position / column in each data set. The values must be the row names (see settings) for order and item data respectively.' => 'Die Spalte "datatype" muss vorhanden sein und sie muss in jedem Datensatz an der gleichen Stelle / Spalte sein. Die Werte in dieser Spalte müssen die Namen der Auftrag-/Positions-Zeilen (siehe Einstellungen) sein.',
|
|
2120 | 2120 |
'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => 'Die Spalte "make_X" can entweder die Datenbank-ID des Lieferanten, eine Lieferantennummer oder einen Lieferantennamen enthalten.', |
2121 | 2121 |
'The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).' => 'Die Spalten-Dreiergruppen können mehrfach auftreten, sofern sie unterschiedliche Nummern "X" verwenden (z.B. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).', |
2122 | 2122 |
'The columns "Dunning Duedate", "Total Fees" and "Interest" show data for the previous dunning created for this invoice.' => 'Die Spalten "Zahlbar bis", "Kumulierte Gebühren" und "Zinsen" zeigen Daten der letzten für diese Rechnung erzeugten Mahnung.', |
locale/en/all | ||
---|---|---|
2079 | 2079 |
'The client has been created.' => '', |
2080 | 2080 |
'The client has been deleted.' => '', |
2081 | 2081 |
'The client has been saved.' => '', |
2082 |
'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => '',
|
|
2082 |
'The column "datatype" must be present and must be at the same position / column in each data set. The values must be the row names (see settings) for order and item data respectively.' => '',
|
|
2083 | 2083 |
'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => '', |
2084 | 2084 |
'The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).' => '', |
2085 | 2085 |
'The columns "Dunning Duedate", "Total Fees" and "Interest" show data for the previous dunning created for this invoice.' => '', |
t/helper/csv.t | ||
---|---|---|
1 |
use Test::More tests => 71;
|
|
1 |
use Test::More tests => 75;
|
|
2 | 2 |
|
3 | 3 |
use lib 't'; |
4 | 4 |
use utf8; |
... | ... | |
551 | 551 |
|
552 | 552 |
##### |
553 | 553 |
|
554 |
$csv = SL::Helper::Csv->new( |
|
555 |
file => \<<EOL, |
|
556 |
description;longdescription;datatype |
|
557 |
name;customernumber;datatype |
|
558 |
Kaffee;"lecker Kaffee";P |
|
559 |
Meier;1;C |
|
560 |
Bier;"kühles Bier";P |
|
561 |
Mueller;2;C |
|
562 |
EOL |
|
563 |
# " # make emacs happy |
|
564 |
profile => [ |
|
565 |
{class => 'SL::DB::Part', row_ident => 'P'}, |
|
566 |
{class => 'SL::DB::Customer', row_ident => 'C'}, |
|
567 |
], |
|
568 |
ignore_unknown_columns => 1, |
|
569 |
); |
|
570 |
$csv->parse; |
|
571 |
is $csv->_multiplex_datatype_position, 2, 'multiplex check detects datatype field position right'; |
|
572 |
|
|
573 |
is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee', longdescription => 'lecker Kaffee' }, |
|
574 |
{ datatype => 'C', name => 'Meier', customernumber => 1}, |
|
575 |
{ datatype => 'P', description => 'Bier', longdescription => 'kühles Bier' }, |
|
576 |
{ datatype => 'C', name => 'Mueller', customernumber => 2} |
|
577 |
], |
|
578 |
'multiplex: datatype not at first position works'; |
|
579 |
|
|
580 |
##### |
|
581 |
|
|
582 |
$csv = SL::Helper::Csv->new( |
|
583 |
file => \<<EOL, |
|
584 |
datatype;description;longdescription |
|
585 |
name;datatype;customernumber |
|
586 |
P;Kaffee;"lecker Kaffee" |
|
587 |
Meier;C;1 |
|
588 |
P;Bier;"kühles Bier" |
|
589 |
Mueller;C;2 |
|
590 |
EOL |
|
591 |
# " # make emacs happy |
|
592 |
profile => [ |
|
593 |
{class => 'SL::DB::Part', row_ident => 'P'}, |
|
594 |
{class => 'SL::DB::Customer', row_ident => 'C'}, |
|
595 |
], |
|
596 |
ignore_unknown_columns => 1, |
|
597 |
); |
|
598 |
ok !$csv->parse, 'multiplex check detects incosistent datatype field position'; |
|
599 |
is_deeply( ($csv->errors)[0], [ 0, 'datatype field must be at the same position for all datatypes for multiplexed data', 0, 0 ], 'multiplex data with inconsistent datatype field posiotion throws error'); |
|
600 |
|
|
601 |
##### |
|
602 |
|
|
554 | 603 |
$csv = SL::Helper::Csv->new( |
555 | 604 |
file => \"Datatype;Description\nDatatype;Name\nP;Kaffee\nC;Meier", # " # make emacs happy |
556 | 605 |
case_insensitive_header => 1, |
templates/webpages/csv_import/form.html | ||
---|---|---|
133 | 133 |
[%- ELSIF SELF.type == 'orders' %] |
134 | 134 |
<p> |
135 | 135 |
[1]: |
136 |
[% LxERP.t8('The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.') %]
|
|
136 |
[% LxERP.t8('The column "datatype" must be present and must be at the same position / column in each data set. The values must be the row names (see settings) for order and item data respectively.') %]
|
|
137 | 137 |
</p> |
138 | 138 |
<p> |
139 | 139 |
[2]: |
Auch abrufbar als: Unified diff
CSV-Import mit Multiplex-Daten: Die Spalte datatype muss nicht an der ersten Position sein.