1 # Copyright 2002-2007 Interchange Development Group and others
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version. See the LICENSE file for details.
8 # $Id: import_fields.coretag,v 1.15 2007-08-03 18:17:24 racke Exp $
10 UserTag import_fields Order table
11 UserTag import_fields addAttr
12 UserTag import_fields Version $Revision: 1.15 $
13 UserTag import_fields Routine <<EOR
15 my($table, $opt) = @_;
18 #::logDebug("options for import_fields: " . ::uneval(\@_) );
20 $SIG{"__DIE__"} = sub {
23 <HTML><HEAD><TITLE>Fatal Administration Error</TITLE></HEAD><BODY>
34 my $file = $opt->{'file'} || $Vend::Cfg->{ProductDir} . "/$table.update";
40 '' => { mv_credit_card_number => 'encrypt' },
43 if($opt->{filter_field}) {
44 my @filt = grep /\S/, split /[\r\n]+/, $opt->{filter_field};
48 my ($t, $f) = split /\s*:\s*/, $_;
50 if ($opt->{multiple}) {
51 die "Must specify both table and filter for multiple table filters.\n";
60 #::logDebug("found filter: t=$t f=$f");
61 my ($field, $filters) = split /\s*=\s*/, $f, 2;
62 #::logDebug("found filter: t=$t field=$field filters=$filters");
63 $filter{$t}{$field} = $filters;
68 last CONVERT if ! $opt->{convert};
69 if ($opt->{convert} eq 'auto') {
70 if($file =~ /\.(txt|all)$/i) {
73 elsif($file =~ /\.xls$/i) {
74 $opt->{convert} = 'xls';
80 return "Failed: unknown file extension ''";
83 elsif ($opt->{convert} eq 'xls') {
84 #::logDebug("doing XLS for file=$file");
86 require Spreadsheet::ParseExcel;
87 import Spreadsheet::ParseExcel;
89 my $oBook = Spreadsheet::ParseExcel::Workbook->Parse($file);
90 #::logDebug("oBook is $oBook");
92 die errmsg("Failed to parse XLS file %s: %s\n", $file, $!);
94 my($iR, $iC, $oWkS, $oWkC);
96 my $sheetcount = $oBook->{SheetCount};
97 #::logDebug("Sheetcount is $sheetcount");
100 for my $oWkS (@{$oBook->{Worksheet}}) {
101 next unless defined $oWkS;
103 for(qw/MaxCol MaxRow MinCol MinRow/) {
104 die "No $_!" if ! defined $oWkS->{$_};
107 my $sname = $oWkS->{Name} or die "no sheet name.";
108 #::logDebug("doing sheet $sname");
109 $sheets->{$sname} = "$sname\n";
115 my $iR = $oWkS->{MinRow};
117 for($iC = $oWkS->{MinCol} ; $iC <= $oWkS->{MaxCol} ; $iC++) {
118 $oWkC = $oWkS->{Cells}[$iR][$iC];
119 if(! $oWkC or ! $oWkC->Value) {
127 $mincol = $oWkS->{MinCol};
130 for( ; $iR <= $oWkS->{MaxRow}; $iR++) {
131 my $row = $oWkS->{Cells}[$iR];
133 for($iC = $mincol; $iC <= $maxcol; $iC++) {
134 if(! defined $row->[$iC]) {
138 push @out, $row->[$iC]->Value;
140 $sheets->{$sname} .= join "\t", @out;
141 $sheets->{$sname} .= "\n";
146 for(sort keys %$sheets) {
147 push @print, $sheets->{$_};
149 $file =~ s/(\.xls)?$/.txt/i;
151 or die "Cannot write $file: $!\n";
152 print OUT join "\cL", @print;
155 die "Excel conversion failed: $@\n" if $@;
158 # other types, or assume gnumeric simple text
164 if($opt->{multiple}) {
168 $Vend::WriteDatabase{$table} = 1;
169 $Vend::TransactionDatabase{$table} = 1
170 if $opt->{transactions};
171 #::logDebug("changing table to $table");
172 $db = Vend::Data::database_exists_ref($table);
173 #::logDebug("db now=$db");
174 die "Non-existent table '$table'\n" unless $db;
176 #::logDebug("db now=$db");
177 if($opt->{autonumber} and ! $db->config('_Auto_number') ) {
178 $db->config('AUTO_NUMBER', '1000');
180 #::logDebug("db now=$db");
181 $tmsg = "table $table: ";
186 $Vend::WriteDatabase{$table} = 1;
187 $Vend::TransactionDatabase{$table} = 1
188 if $opt->{transactions};
189 $db = Vend::Data::database_exists_ref($table);
190 die "Non-existent table '$table'\n" unless $db;
191 $db = $db->ref() unless $Vend::Interpolate::Db{$table};
192 if($opt->{autonumber} and ! $db->config('_Auto_number') ) {
193 $db->config('AUTO_NUMBER', '1000');
198 my $delimiter = quotemeta $opt->{delimiter} || "\t";
200 or die "read $file: $!\n";
204 if($opt->{multiple}) {
205 # will get fields later
206 undef $opt->{fields};
208 elsif($opt->{fields}) {
209 $fields = $opt->{fields};
210 $out .= "Using fields from parameter: '$fields'\n";
216 $verbose = 1 if ! $opt->{quiet};
217 $quiet = 1 if $opt->{quiet} > 1;
222 $table =~ s/(\015\012|\015|\012)$//;
223 $change_sub->($table);
225 #::logDebug("db now=$db");
226 if(! $opt->{fields}) {
228 $fields =~ s/(\015\012|\015|\012)$//;
229 $fields =~ s/$delimiter/ /g;
230 $out .= "${tmsg}Using fields from file: '$fields'\n";
232 $filter{$table} ||= {};
233 die "No field names." if ! $fields;
237 @names = split /\s+/, $fields;
238 my $key = shift @names;
244 if ($key !~ /^[\w_-]+$/) {
245 die "Invalid key '$key' for table $table (wrong file format ?)\n";
248 my $multikey = $db->config('COMPOSITE_KEY') ? 1 : 0;
251 if ($opt->{ignore_fields}) {
253 for (my $ct = 0; $ct < @names; $ct++) {
254 $fmap{$names[$ct]} = $ct;
256 for (split(/[\0\s,]+/, $opt->{ignore_fields})) {
259 my $code = 'sub {$a = shift; @$a = @$a[' . join(',', values(%fmap)) . '];}';
260 $ignore_sub = eval $code;
261 die "Routine to ignore fields bad: $@" if $@;
262 @names = grep {exists $fmap{$_}} @names;
265 # We skip the whole table if bad field is found
272 @fmap{$key,@names} = ($key,@names);
274 for(@{$db->config('_Key_columns')}) {
281 "Table %s: not all key columns present. Skipping table.",
291 ## Done with so many data items for speed when empty....
294 ## Holds filter subroutines if any
296 ## Holds names of filter subroutines if any
298 ## Non-zero if found any filter
299 my $found_filter = 0;
304 my $test = $db->column_index($_);
305 #::logDebug("checking name=$_");
306 if(! defined $test) {
308 "Table %s: undefined column '%s'. Skipping table.",
314 elsif ($filter{''}{$_} || $filter{$table}{$_}) {
315 #::logDebug("found filter for name=$_");
316 my @things = grep length($_), $filter{''}{$_}, $filter{$table}{$_};
317 my $thing = join " ", @things;
321 $$ref = Vend::Interpolate::filter_value($thing, $$ref);
326 "Table %s: unrequited filter '%s'. Skipping table.",
338 if ($opt->{cleanse}) {
339 # record existing columns
342 $recs = $db->query("select " . join(',', @keycols) . " from $table");
343 $keys{join("\0", @$_)} = 1 for @$recs;
345 $recs = $db->query("select $key from $table");
346 $keys{$_->[0]} = 1 for @$recs;
354 s/(\015\012|\015|\012)$//;
356 ($k, @f) = split /$delimiter/o, $_;
358 $out .= "${tmsg}$count records processed of $totcount input lines.\n";
359 $out .= "${tmsg}$delcount records deleted.\n" if $delcount;
360 $out .= "${tmsg}$addcount records added.\n" if $addcount;
361 $delcount = $totcount = $addcount = 0;
362 $db->commit() if $opt->{transactions};
367 if(! $k and ! length($k)) {
368 if ($f[0] eq 'DELETE') {
369 next if ! $opt->{delete};
371 $out .= "${tmsg}Deleting record '$f[1]'.\n" if $verbose;
372 $db->delete_record($f[1]);
378 $ignore_sub->(\@f) if $ignore_sub;
379 $out .= "${tmsg}Record '$k' had too many fields, ignored.\n"
386 $change{$_}->(\$hash{$_});
392 if(! $db->record_exists(\%hash)) {
394 $out .= "${tmsg}Adding multiple-key record.\n" if $verbose;
397 $out .= "${tmsg}Non-existent record '$k', skipping.\n";
403 elsif ( ! length($k) or ! $db->record_exists($k)) {
405 if( ! length($k) and ! $opt->{autonumber}) {
406 $out .= "${tmsg}Blank key, no autonumber option, skipping.\n";
409 $k = $db->set_row($k);
410 $out .= "${tmsg}Adding record '$k'.\n" if $verbose;
414 $out .= "${tmsg}Non-existent record '$k', skipping.\n";
419 if ($opt->{cleanse}) {
421 delete $keys{join("\0", map{$hash{$_}} @keycols)};
427 $db->set_slice($k, \%hash) if @names;
430 my $msg = ::errmsg("error on update: %s", $@);
437 $db->commit() if $opt->{transactions};
439 if ($opt->{cleanse}) {
440 # remove any record which hasn't updated
442 $db->delete_record($_);
446 $out .= "${tmsg}$count records processed of $totcount input lines.\n";
447 $out .= "${tmsg}$delcount records deleted.\n" if $delcount;
448 $out .= "${tmsg}$addcount records added.\n" if $addcount;
453 my $ext = POSIX::strftime("%Y%m%d%H%M%S", localtime());
454 rename $file, "$file.$ext"
455 or die "rename $file --> $file.$ext: $!\n";
457 and (-d $opt->{dir} or File::Path::mkpath($opt->{dir}))
461 File::Copy::move("$file.$ext", $opt->{dir})
462 or die "move $file.$ext --> $opt->{dir}: $!\n";
465 return $out unless $quiet;