have freeside-queued put billing jobs in the queue, so they run in their own short...
[freeside.git] / FS / FS / part_pkg_taxrate.pm
index 3e7e7bd..9e1c723 100644 (file)
@@ -6,6 +6,7 @@ use Date::Parse;
 use FS::UID qw(dbh);
 use FS::Record qw( qsearch qsearchs );
 use FS::part_pkg_taxproduct;
+use FS::Misc qw(csv_from_fixed);
 
 @ISA = qw(FS::Record);
 
@@ -150,7 +151,7 @@ sub check {
     || $self->ut_text('country')
     || $self->ut_foreign_keyn('taxclassnumtaxed', 'tax_class', 'taxclassnum')
     || $self->ut_foreign_key('taxclassnum', 'tax_class', 'taxclassnum')
-    || $self->ut_numbern('effective_date')
+    || $self->ut_snumbern('effdate')
     || $self->ut_enum('taxable', [ 'Y', '' ])
   ;
   return $error if $error;
@@ -166,17 +167,43 @@ an error, returns the error, otherwise returns false.
 =cut 
 
 sub batch_import {
-  my $param = shift;
+  my ($param, $job) = @_;
 
   my $fh = $param->{filehandle};
   my $format = $param->{'format'};
 
+  my $imported = 0;
   my @fields;
   my $hook;
-  if ( $format eq 'cch' ) {
+
+  my @column_lengths = ();
+  my @column_callbacks = ();
+  if ( $format eq 'cch-fixed' || $format eq 'cch-fixed-update' ) {
+    $format =~ s/-fixed//;
+    my $date_format = sub { my $r='';
+                            /^(\d{4})(\d{2})(\d{2})$/ && ($r="$1/$2/$3");
+                            $r;
+                          };
+    $column_callbacks[16] = $date_format;
+    push @column_lengths, qw( 28 25 2 1 10 4 30 3 100 2 2 2 2 1 2 2 8 1 );
+    push @column_lengths, 1 if $format eq 'cch-update';
+  }
+
+  my $line;
+  my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
+  if ( $job || scalar(@column_callbacks) ) {
+    my $error =
+      csv_from_fixed(\$fh, \$count, \@column_lengths, \@column_callbacks);
+    return $error if $error;
+  }
+
+  if ( $format eq 'cch' ||  $format eq 'cch-update' ) {
     @fields = qw( city county state local geocode group groupdesc item
                   itemdesc provider customer taxtypetaxed taxcattaxed
                   taxable taxtype taxcat effdate rectype );
+    push @fields, 'actionflag' if $format eq 'cch-update';
+
+    $imported++ if $format eq 'cch-update';  #empty file ok
 
     $hook = sub { 
       my $hash = shift;
@@ -186,6 +213,8 @@ sub batch_import {
         return;
       }
 
+      $hash->{'data_vendor'} = 'cch';
+
       my %providers = ( '00' => 'Regulated LEC',
                         '01' => 'Regulated IXC',
                         '02' => 'Unregulated LEC',
@@ -213,6 +242,10 @@ sub batch_import {
                                         );
 
       unless ($part_pkg_taxproduct) {
+        return "Can't find part_pkg_taxproduct for txmatrix deletion: ".
+               join(" ", map { "$_ => ". $hash->{$_} } @fields)
+          if ($hash->{'actionfield'} && $hash->{'actionflag'} eq 'D');
+
         $part_pkg_taxproduct{'description'} = 
           join(' : ', (map{ $hash->{$_} } qw(groupdesc itemdesc)),
                       $providers{$hash->{'provider'}},
@@ -234,25 +267,47 @@ sub batch_import {
                 );
 
       for my $item (keys %map) {
+        my $class = join(':', map($hash->{$_}, @{$map{$item}}));
         my $tax_class =
           qsearchs( 'tax_class',
                     { data_vendor => 'cch',
-                      'taxclass' => join(':', map($hash->{$_}, @{$map{$item}})),
+                      'taxclass' => $class,
                     }
                   );
         $hash->{$item} = $tax_class->taxclassnum
           if $tax_class;
 
+        return "Can't find tax class for txmatrix deletion: ".
+               join(" ", map { "$_ => ". $hash->{$_} } @fields)
+          if ( $hash->{'actionflag'} && $hash->{'actionflag'} eq 'D' &&
+               !$tax_class && $class ne ':'
+             );
+
         delete($hash->{$_}) foreach @{$map{$item}};
       }
 
       $hash->{'effdate'} = str2time($hash->{'effdate'});
-
-      $hash->{'effdate'} = str2time($hash->{'effdate'});
       $hash->{'country'} = 'US'; # CA is available
 
       delete($hash->{'taxable'}) if ($hash->{'taxable'} eq 'N');
 
+      if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
+        delete($hash->{actionflag});
+
+        my $part_pkg_taxrate = qsearchs('part_pkg_taxrate', $hash);
+        return "Can't find part_pkg_taxrate to delete: ".
+               #join(" ", map { "$_ => ". $hash->{$_} } @fields)
+               join(" ", map { "$_ => *". $hash->{$_}. '*' } keys(%$hash) )
+          unless $part_pkg_taxrate;
+
+        my $error = $part_pkg_taxrate->delete;
+        return $error if $error;
+
+        delete($hash->{$_}) foreach (keys %$hash);
+      }
+
+      delete($hash->{actionflag});
+
       '';
     };
 
@@ -269,8 +324,6 @@ sub batch_import {
 
   my $csv = new Text::CSV_XS;
 
-  my $imported = 0;
-
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE';
@@ -282,19 +335,34 @@ sub batch_import {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
   
-  my $line;
   while ( defined($line=<$fh>) ) {
     $csv->parse($line) or do {
       $dbh->rollback if $oldAutoCommit;
       return "can't parse: ". $csv->error_input();
     };
 
+
+    if ( $job ) {  # progress bar
+      if ( time - $min_sec > $last ) {
+        my $error = $job->update_statustext(
+          int( 100 * $imported / $count )
+        );
+        die $error if $error;
+        $last = time;
+      }
+    }
+
     my @columns = $csv->fields();
 
     my %part_pkg_taxrate = ( 'data_vendor' => $format );
     foreach my $field ( @fields ) {
       $part_pkg_taxrate{$field} = shift @columns; 
     }
+    if ( scalar( @columns ) ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Unexpected trailing columns in line (wrong format?): $line";
+    }
+
     my $error = &{$hook}(\%part_pkg_taxrate);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;