+ my $hook;
+
+ my @column_lengths = ();
+ my @column_callbacks = ();
+ if ( $format =~ /^cch-fixed/ ) {
+ $format =~ s/-fixed//;
+ my $f = $format;
+ my $update = 0;
+ $f =~ s/-update// && ($update = 1);
+ if ($f eq 'cch') {
+ push @column_lengths, qw( 5 2 4 4 10 1 );
+ } elsif ( $f eq 'cch-zip' ) {
+ push @column_lengths, qw( 5 28 25 2 28 5 1 1 10 1 2 );
+ } else {
+ return "Unknown format: $format";
+ }
+ push @column_lengths, 1 if $update;
+ }
+
+ my $line;
+ my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+ if ( $job || scalar(@column_lengths) ) {
+ my $error = csv_from_fixed(\$fh, \$count, \@column_lengths);
+ return $error if $error;
+ }
+
+ if ( $format eq 'cch' || $format eq 'cch-update' ) {
+ @fields = qw( zip state plus4lo plus4hi geocode default_location );
+ push @fields, 'actionflag' if $format eq 'cch-update';
+
+ $imported++ if $format eq 'cch-update'; #empty file ok
+
+ $hook = sub {
+ my $hash = shift;
+
+ $hash->{'data_vendor'} = 'cch';
+ $hash->{'default_location'} =~ s/ //g;
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ delete($hash->{actionflag});
+
+ my @cust_tax_location = qsearch('cust_tax_location', $hash);
+ return "Can't find cust_tax_location to delete: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ unless scalar(@cust_tax_location) || $param->{'delete_only'} ;
+
+ foreach my $cust_tax_location (@cust_tax_location) {
+ my $error = $cust_tax_location->delete;
+ return $error if $error;
+ }
+
+ delete($hash->{$_}) foreach (keys %$hash);
+ }
+
+ delete($hash->{'actionflag'});
+
+ '';
+
+ };
+
+ } elsif ( $format eq 'cch-zip' || $format eq 'cch-update-zip' ) {
+ @fields = qw( zip city county state postalcity countyfips countydef default_location geocode cityflag unique );
+ push @fields, 'actionflag' if $format eq 'cch-update-zip';
+
+ $imported++ if $format eq 'cch-update'; #empty file ok
+
+ $hook = sub {
+ my $hash = shift;
+
+ $hash->{'data_vendor'} = 'cch-zip';
+ delete($hash->{$_}) foreach qw( countyfips countydef unique );
+
+ $hash->{'cityflag'} =~ s/ //g;
+ $hash->{'default_location'} =~ s/ //g;
+
+ if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+ delete($hash->{actionflag});
+
+ my @cust_tax_location = qsearch('cust_tax_location', $hash);
+ return "Can't find cust_tax_location to delete: ".
+ join(" ", map { "$_ => ". $hash->{$_} } @fields)
+ unless scalar(@cust_tax_location) || $param->{'delete_only'} ;
+
+ foreach my $cust_tax_location (@cust_tax_location) {
+ my $error = $cust_tax_location->delete;
+ return $error if $error;
+ }
+
+ delete($hash->{$_}) foreach (keys %$hash);
+ }
+
+ delete($hash->{'actionflag'});
+
+ '';
+
+ };
+
+ } elsif ( $format eq 'billsoft' ) {
+
+ @fields = qw( geocode alt_location country state county city
+ ziplo ziphi );
+ $hook = sub {
+ my $hash = shift;
+ $hash->{data_vendor} = 'billsoft';
+ $hash->{default_location} = ($hash->{alt_location} ? '' : 'Y');
+ $hash->{city} =~ s/[^\w ]//g; # remove asterisks and other bad things
+ $hash->{country} = substr($hash->{country}, 0, 2);
+ if ( $hash->{state} =~ /^ *$/
+ or $hash->{county} =~ /^ *$/
+ or $hash->{country} !~ /^US|CA$/ ) {
+ # remove whole-country rows, whole-state rows, and non-CAN/USA rows
+ %$hash = ();
+ }
+ '';
+ };
+