diff options
| -rw-r--r-- | FS/FS.pm | 2 | ||||
| -rw-r--r-- | FS/FS/Mason.pm | 1 | ||||
| -rw-r--r-- | FS/FS/cust_main.pm | 344 | ||||
| -rw-r--r-- | FS/FS/cust_main/Import.pm | 425 | ||||
| -rw-r--r-- | FS/FS/part_pkg.pm | 20 | ||||
| -rw-r--r-- | FS/MANIFEST | 1 | ||||
| -rw-r--r-- | FS/bin/freeside-queued | 2 | ||||
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | httemplate/misc/cust_main-import.cgi | 4 | ||||
| -rw-r--r-- | httemplate/misc/process/cust_main-import.cgi | 3 | 
10 files changed, 456 insertions, 349 deletions
| @@ -61,6 +61,8 @@ L<FS::ClientAPI_SessionCache> - ClientAPI session cache  L<FS::Pony> - A pony +L<FS::cust_main::Import> - Batch customer importing +  =head2 Database record classes  L<FS::Record> - Database record base class diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index cfb12e4ae..0a608ddb0 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -103,6 +103,7 @@ Initializes the Mason environment, loads all Freeside and RT libraries, etc.    use FS::cust_credit;    use FS::cust_credit_bill;    use FS::cust_main qw(smart_search); +  use FS::cust_main::Import;    use FS::cust_main_county;    use FS::cust_pay;    use FS::cust_pkg; diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index d5b45b0f5..5554f9f48 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -14,9 +14,7 @@ use Data::Dumper;  use Tie::IxHash;  use Digest::MD5 qw(md5_base64);  use Date::Format; -use Date::Parse;  #use Date::Manip; -use File::Slurp qw( slurp );  use File::Temp qw( tempfile );  use String::Approx qw(amatch);  use Business::CreditCard 0.28; @@ -6355,348 +6353,6 @@ sub append_fuzzyfiles {    1;  } -=item process_batch_import - -Load a batch import as a queued JSRPC job - -=cut - -use Storable qw(thaw); -use Data::Dumper; -use MIME::Base64; -sub process_batch_import { -  my $job = shift; - -  my $param = thaw(decode_base64(shift)); -  warn Dumper($param) if $DEBUG; -   -  my $files = $param->{'uploaded_files'} -    or die "No files provided.\n"; - -  my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files; - -  my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/'; -  my $file = $dir. $files{'file'}; - -  my $type; -  if ( $file =~ /\.(\w+)$/i ) { -    $type = lc($1); -  } else { -    #or error out??? -    warn "can't parse file type from filename $file; defaulting to CSV"; -    $type = 'csv'; -  } - -  my $error = -    FS::cust_main::batch_import( { -      job       => $job, -      file      => $file, -      type      => $type, -      custbatch => $param->{custbatch}, -      agentnum  => $param->{'agentnum'}, -      refnum    => $param->{'refnum'}, -      pkgpart   => $param->{'pkgpart'}, -      #'fields'  => [qw( cust_pkg.setup dayphone first last address1 address2 -      #                 city state zip comments                          )], -      'format'  => $param->{'format'}, -    } ); - -  unlink $file; - -  die "$error\n" if $error; - -} - -=item batch_import - -=cut - -use FS::svc_acct; -use FS::svc_external; - -#some false laziness w/cdr.pm now -sub batch_import { -  my $param = shift; - -  my $job       = $param->{job}; - -  my $filename  = $param->{file}; -  my $type      = $param->{type} || 'csv'; - -  my $custbatch = $param->{custbatch}; - -  my $agentnum  = $param->{agentnum}; -  my $refnum    = $param->{refnum}; -  my $pkgpart   = $param->{pkgpart}; - -  my $format    = $param->{'format'}; - -  my @fields; -  my $payby; -  if ( $format eq 'simple' ) { -    @fields = qw( cust_pkg.setup dayphone first last -                  address1 address2 city state zip comments ); -    $payby = 'BILL'; -  } elsif ( $format eq 'extended' ) { -    @fields = qw( agent_custid refnum -                  last first address1 address2 city state zip country -                  daytime night -                  ship_last ship_first ship_address1 ship_address2 -                  ship_city ship_state ship_zip ship_country -                  payinfo paycvv paydate -                  invoicing_list -                  cust_pkg.pkgpart -                  svc_acct.username svc_acct._password  -                ); -    $payby = 'BILL'; - } elsif ( $format eq 'extended-plus_company' ) { -    @fields = qw( agent_custid refnum -                  last first company address1 address2 city state zip country -                  daytime night -                  ship_last ship_first ship_company ship_address1 ship_address2 -                  ship_city ship_state ship_zip ship_country -                  payinfo paycvv paydate -                  invoicing_list -                  cust_pkg.pkgpart -                  svc_acct.username svc_acct._password  -                ); -    $payby = 'BILL'; - } elsif ( $format eq 'svc_external' ) { -    @fields = qw( agent_custid refnum -                  last first company address1 address2 city state zip country -                  daytime night -                  ship_last ship_first ship_company ship_address1 ship_address2 -                  ship_city ship_state ship_zip ship_country -                  payinfo paycvv paydate -                  invoicing_list -                  cust_pkg.pkgpart cust_pkg.bill -                  svc_external.id svc_external.title -                ); -    $payby = 'BILL'; -  } else { -    die "unknown format $format"; -  } - -  my $count; -  my $parser; -  my @buffer = (); -  if ( $type eq 'csv' ) { - -    eval "use Text::CSV_XS;"; -    die $@ if $@; - -    $parser = new Text::CSV_XS; - -    @buffer = split(/\r?\n/, slurp($filename) ); -    $count = scalar(@buffer); - -  } elsif ( $type eq 'xls' ) { - -    eval "use Spreadsheet::ParseExcel;"; -    die $@ if $@; - -    my $excel = new Spreadsheet::ParseExcel::Workbook->Parse($filename); -    $parser = $excel->{Worksheet}[0]; #first sheet - -    $count = $parser->{MaxRow} || $parser->{MinRow}; -    $count++; - -  } else { -    die "Unknown file type $type\n"; -  } - -  #my $columns; - -  local $SIG{HUP} = 'IGNORE'; -  local $SIG{INT} = 'IGNORE'; -  local $SIG{QUIT} = 'IGNORE'; -  local $SIG{TERM} = 'IGNORE'; -  local $SIG{TSTP} = 'IGNORE'; -  local $SIG{PIPE} = 'IGNORE'; - -  my $oldAutoCommit = $FS::UID::AutoCommit; -  local $FS::UID::AutoCommit = 0; -  my $dbh = dbh; -   -  my $line; -  my $row = 0; -  my( $last, $min_sec ) = ( time, 5 ); #progressbar foo -  while (1) { - -    my @columns = (); -    if ( $type eq 'csv' ) { - -      last unless scalar(@buffer); -      $line = shift(@buffer); - -      $parser->parse($line) or do { -        $dbh->rollback if $oldAutoCommit; -        return "can't parse: ". $parser->error_input(); -      }; -      @columns = $parser->fields(); - -    } elsif ( $type eq 'xls' ) { - -      last if $row > ($parser->{MaxRow} || $parser->{MinRow}); - -      my @row = @{ $parser->{Cells}[$row] }; -      @columns = map $_->{Val}, @row; - -      #my $z = 'A'; -      #warn $z++. ": $_\n" for @columns; - -    } else { -      die "Unknown file type $type\n"; -    } - -    #warn join('-',@columns); - -    my %cust_main = ( -      custbatch => $custbatch, -      agentnum  => $agentnum, -      refnum    => $refnum, -      country   => $conf->config('countrydefault') || 'US', -      payby     => $payby, #default -      paydate   => '12/2037', #default -    ); -    my $billtime = time; -    my %cust_pkg = ( pkgpart => $pkgpart ); -    my %svc_x = (); -    foreach my $field ( @fields ) { - -      if ( $field =~ /^cust_pkg\.(pkgpart|setup|bill|susp|adjourn|expire|cancel)$/ ) { - -        #$cust_pkg{$1} = str2time( shift @$columns ); -        if ( $1 eq 'pkgpart' ) { -          $cust_pkg{$1} = shift @columns; -        } elsif ( $1 eq 'setup' ) { -          $billtime = str2time(shift @columns); -        } else { -          $cust_pkg{$1} = str2time( shift @columns ); -        }  - -      } elsif ( $field =~ /^svc_acct\.(username|_password)$/ ) { - -        $svc_x{$1} = shift @columns; - -      } elsif ( $field =~ /^svc_external\.(id|title)$/ ) { - -        $svc_x{$1} = shift @columns; -         -      } else { - -        #refnum interception -        if ( $field eq 'refnum' && $columns[0] !~ /^\s*(\d+)\s*$/ ) { - -          my $referral = $columns[0]; -          my %hash = ( 'referral' => $referral, -                       'agentnum' => $agentnum, -                       'disabled' => '', -                     ); - -          my $part_referral = qsearchs('part_referral', \%hash ) -                              || new FS::part_referral \%hash; - -          unless ( $part_referral->refnum ) { -            my $error = $part_referral->insert; -            if ( $error ) { -              $dbh->rollback if $oldAutoCommit; -              return "can't auto-insert advertising source: $referral: $error"; -            } -          } - -          $columns[0] = $part_referral->refnum; -        } - -        my $value = shift @columns; -        $cust_main{$field} = $value if length($value); -      } -    } - -    $cust_main{'payby'} = 'CARD' -      if defined $cust_main{'payinfo'} -      && length  $cust_main{'payinfo'}; - -    my $invoicing_list = $cust_main{'invoicing_list'} -                           ? [ delete $cust_main{'invoicing_list'} ] -                           : []; - -    my $cust_main = new FS::cust_main ( \%cust_main ); - -    use Tie::RefHash; -    tie my %hash, 'Tie::RefHash'; #this part is important - -    if ( $cust_pkg{'pkgpart'} ) { -      my $cust_pkg = new FS::cust_pkg ( \%cust_pkg ); - -      my @svc_x = (); -      my $svcdb = ''; -      if ( $svc_x{'username'} ) { -        $svcdb = 'svc_acct'; -      } elsif ( $svc_x{'id'} || $svc_x{'title'} ) { -        $svcdb = 'svc_external'; -      } -      if ( $svcdb ) { -        my $part_pkg = $cust_pkg->part_pkg; -	unless ( $part_pkg ) { -	  $dbh->rollback if $oldAutoCommit; -	  return "unknown pkgpart: ". $cust_pkg{'pkgpart'}; -	}  -        $svc_x{svcpart} = $part_pkg->svcpart( $svcdb ); -        my $class = "FS::$svcdb"; -        push @svc_x, $class->new( \%svc_x ); -      } - -      $hash{$cust_pkg} = \@svc_x; -    } - -    my $error = $cust_main->insert( \%hash, $invoicing_list ); - -    if ( $error ) { -      $dbh->rollback if $oldAutoCommit; -      return "can't insert customer". ( $line ? " for $line" : '' ). ": $error"; -    } - -    if ( $format eq 'simple' ) { - -      #false laziness w/bill.cgi -      $error = $cust_main->bill( 'time' => $billtime ); -      if ( $error ) { -        $dbh->rollback if $oldAutoCommit; -        return "can't bill customer for $line: $error"; -      } -   -      $error = $cust_main->apply_payments_and_credits; -      if ( $error ) { -        $dbh->rollback if $oldAutoCommit; -        return "can't bill customer for $line: $error"; -      } - -      $error = $cust_main->collect(); -      if ( $error ) { -        $dbh->rollback if $oldAutoCommit; -        return "can't collect customer for $line: $error"; -      } - -    } - -    $row++; - -    if ( $job && time - $min_sec > $last ) { #progress bar -      $job->update_statustext( int(100 * $row / $count) ); -      $last = time; -    } - -  } - -  $dbh->commit or die $dbh->errstr if $oldAutoCommit;; - -  return "Empty file!" unless $row; - -  ''; #no error - -} -  =item batch_charge  =cut diff --git a/FS/FS/cust_main/Import.pm b/FS/FS/cust_main/Import.pm new file mode 100644 index 000000000..4f377b9b4 --- /dev/null +++ b/FS/FS/cust_main/Import.pm @@ -0,0 +1,425 @@ +package FS::cust_main::Import; + +use strict; +use vars qw( $DEBUG $conf ); +use Storable qw(thaw); +use Data::Dumper; +use MIME::Base64; +use Date::Parse; +use File::Slurp qw( slurp ); +use FS::UID qw( dbh ); +use FS::cust_main; +use FS::svc_acct; +use FS::svc_external; +use FS::svc_phone; + +$DEBUG = 0; + +install_callback FS::UID sub { +  $conf = new FS::Conf; +}; + +=head1 NAME + +FS::cust_main::Import - Batch customer importing + +=head1 SYNOPSIS + +  use FS::cust_main::Import; + +  #import +  FS::cust_main::Import::batch_import( { +    file      => $file,      #filename +    type      => $type,      #csv or xls +    format    => $format,    #extended, extended-plus_company, svc_external, +                             # or svc_external_svc_phone +    agentnum  => $agentnum, +    refnum    => $refnum, +    pkgpart   => $pkgpart, +    job       => $job,       #optional job queue job, for progressbar updates +    custbatch => $custbatch, #optional batch unique identifier +  } ); +  die $error if $error; + +  #ajax helper +  use FS::UI::Web::JSRPC; +  my $server = +    new FS::UI::Web::JSRPC 'FS::cust_main::Import::process_batch_import', $cgi; +  print $server->process; + +=head1 DESCRIPTION + +Batch customer importing. + +=head1 SUBROUTINES + +=item process_batch_import + +Load a batch import as a queued JSRPC job + +=cut + +sub process_batch_import { +  my $job = shift; + +  my $param = thaw(decode_base64(shift)); +  warn Dumper($param) if $DEBUG; +   +  my $files = $param->{'uploaded_files'} +    or die "No files provided.\n"; + +  my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files; + +  my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/'; +  my $file = $dir. $files{'file'}; + +  my $type; +  if ( $file =~ /\.(\w+)$/i ) { +    $type = lc($1); +  } else { +    #or error out??? +    warn "can't parse file type from filename $file; defaulting to CSV"; +    $type = 'csv'; +  } + +  my $error = +    FS::cust_main::Import::batch_import( { +      job       => $job, +      file      => $file, +      type      => $type, +      custbatch => $param->{custbatch}, +      agentnum  => $param->{'agentnum'}, +      refnum    => $param->{'refnum'}, +      pkgpart   => $param->{'pkgpart'}, +      #'fields'  => [qw( cust_pkg.setup dayphone first last address1 address2 +      #                 city state zip comments                          )], +      'format'  => $param->{'format'}, +    } ); + +  unlink $file; + +  die "$error\n" if $error; + +} + +=item batch_import + +=cut + + +#some false laziness w/cdr.pm now +sub batch_import { +  my $param = shift; + +  my $job       = $param->{job}; + +  my $filename  = $param->{file}; +  my $type      = $param->{type} || 'csv'; + +  my $custbatch = $param->{custbatch}; + +  my $agentnum  = $param->{agentnum}; +  my $refnum    = $param->{refnum}; +  my $pkgpart   = $param->{pkgpart}; + +  my $format    = $param->{'format'}; + +  my @fields; +  my $payby; +  if ( $format eq 'simple' ) { +    @fields = qw( cust_pkg.setup dayphone first last +                  address1 address2 city state zip comments ); +    $payby = 'BILL'; +  } elsif ( $format eq 'extended' ) { +    @fields = qw( agent_custid refnum +                  last first address1 address2 city state zip country +                  daytime night +                  ship_last ship_first ship_address1 ship_address2 +                  ship_city ship_state ship_zip ship_country +                  payinfo paycvv paydate +                  invoicing_list +                  cust_pkg.pkgpart +                  svc_acct.username svc_acct._password  +                ); +    $payby = 'BILL'; + } elsif ( $format eq 'extended-plus_company' ) { +    @fields = qw( agent_custid refnum +                  last first company address1 address2 city state zip country +                  daytime night +                  ship_last ship_first ship_company ship_address1 ship_address2 +                  ship_city ship_state ship_zip ship_country +                  payinfo paycvv paydate +                  invoicing_list +                  cust_pkg.pkgpart +                  svc_acct.username svc_acct._password  +                ); +    $payby = 'BILL'; + } elsif ( $format =~ /^svc_external/ ) { +    @fields = qw( agent_custid refnum +                  last first company address1 address2 city state zip country +                  daytime night +                  ship_last ship_first ship_company ship_address1 ship_address2 +                  ship_city ship_state ship_zip ship_country +                  payinfo paycvv paydate +                  invoicing_list +                  cust_pkg.pkgpart cust_pkg.bill +                  svc_external.id svc_external.title +                ); +    push @fields, map "svc_phone.$_", qw( countrycode phonenum sip_password pin) +      if $format eq 'svc_external_svc_phone'; +    $payby = 'BILL'; +  } else { +    die "unknown format $format"; +  } + +  my $count; +  my $parser; +  my @buffer = (); +  if ( $type eq 'csv' ) { + +    eval "use Text::CSV_XS;"; +    die $@ if $@; + +    $parser = new Text::CSV_XS; + +    @buffer = split(/\r?\n/, slurp($filename) ); +    $count = scalar(@buffer); + +  } elsif ( $type eq 'xls' ) { + +    eval "use Spreadsheet::ParseExcel;"; +    die $@ if $@; + +    my $excel = new Spreadsheet::ParseExcel::Workbook->Parse($filename); +    $parser = $excel->{Worksheet}[0]; #first sheet + +    $count = $parser->{MaxRow} || $parser->{MinRow}; +    $count++; + +  } else { +    die "Unknown file type $type\n"; +  } + +  #my $columns; + +  local $SIG{HUP} = 'IGNORE'; +  local $SIG{INT} = 'IGNORE'; +  local $SIG{QUIT} = 'IGNORE'; +  local $SIG{TERM} = 'IGNORE'; +  local $SIG{TSTP} = 'IGNORE'; +  local $SIG{PIPE} = 'IGNORE'; + +  my $oldAutoCommit = $FS::UID::AutoCommit; +  local $FS::UID::AutoCommit = 0; +  my $dbh = dbh; +   +  my $line; +  my $row = 0; +  my( $last, $min_sec ) = ( time, 5 ); #progressbar foo +  while (1) { + +    my @columns = (); +    if ( $type eq 'csv' ) { + +      last unless scalar(@buffer); +      $line = shift(@buffer); + +      $parser->parse($line) or do { +        $dbh->rollback if $oldAutoCommit; +        return "can't parse: ". $parser->error_input(); +      }; +      @columns = $parser->fields(); + +    } elsif ( $type eq 'xls' ) { + +      last if $row > ($parser->{MaxRow} || $parser->{MinRow}); + +      my @row = @{ $parser->{Cells}[$row] }; +      @columns = map $_->{Val}, @row; + +      #my $z = 'A'; +      #warn $z++. ": $_\n" for @columns; + +    } else { +      die "Unknown file type $type\n"; +    } + +    #warn join('-',@columns); + +    my %cust_main = ( +      custbatch => $custbatch, +      agentnum  => $agentnum, +      refnum    => $refnum, +      country   => $conf->config('countrydefault') || 'US', +      payby     => $payby, #default +      paydate   => '12/2037', #default +    ); +    my $billtime = time; +    my %cust_pkg = ( pkgpart => $pkgpart ); +    my %svc_x = (); +    foreach my $field ( @fields ) { + +      if ( $field =~ /^cust_pkg\.(pkgpart|setup|bill|susp|adjourn|expire|cancel)$/ ) { + +        #$cust_pkg{$1} = str2time( shift @$columns ); +        if ( $1 eq 'pkgpart' ) { +          $cust_pkg{$1} = shift @columns; +        } elsif ( $1 eq 'setup' ) { +          $billtime = str2time(shift @columns); +        } else { +          $cust_pkg{$1} = str2time( shift @columns ); +        }  + +      } elsif ( $field =~ /^svc_acct\.(username|_password)$/ ) { + +        $svc_x{$1} = shift @columns; + +      } elsif ( $field =~ /^svc_external\.(id|title)$/ ) { + +        $svc_x{$1} = shift @columns; + +      } elsif ( $field =~ /^svc_phone\.(countrycode|phonenum|sip_password|pin)$/ ) { +        $svc_x{$1} = shift @columns; +        +      } else { + +        #refnum interception +        if ( $field eq 'refnum' && $columns[0] !~ /^\s*(\d+)\s*$/ ) { + +          my $referral = $columns[0]; +          my %hash = ( 'referral' => $referral, +                       'agentnum' => $agentnum, +                       'disabled' => '', +                     ); + +          my $part_referral = qsearchs('part_referral', \%hash ) +                              || new FS::part_referral \%hash; + +          unless ( $part_referral->refnum ) { +            my $error = $part_referral->insert; +            if ( $error ) { +              $dbh->rollback if $oldAutoCommit; +              return "can't auto-insert advertising source: $referral: $error"; +            } +          } + +          $columns[0] = $part_referral->refnum; +        } + +        my $value = shift @columns; +        $cust_main{$field} = $value if length($value); +      } +    } + +    $cust_main{'payby'} = 'CARD' +      if defined $cust_main{'payinfo'} +      && length  $cust_main{'payinfo'}; + +    my $invoicing_list = $cust_main{'invoicing_list'} +                           ? [ delete $cust_main{'invoicing_list'} ] +                           : []; + +    my $cust_main = new FS::cust_main ( \%cust_main ); + +    use Tie::RefHash; +    tie my %hash, 'Tie::RefHash'; #this part is important + +    if ( $cust_pkg{'pkgpart'} ) { +      my $cust_pkg = new FS::cust_pkg ( \%cust_pkg ); + +      my @svc_x = (); +      my $svcdb = ''; +      if ( $svc_x{'username'} ) { +        $svcdb = 'svc_acct'; +      } elsif ( $svc_x{'id'} || $svc_x{'title'} ) { +        $svcdb = 'svc_external'; +      } + +      my $svc_phone = ''; +      if ( $svc_x{'countrycode'} || $svc_x{'phonenum'} ) { +        $svc_phone = FS::svc_phone->new( { +          map { $_ => delete($svc_x{$_}) } +              qw( countrycode phonenum sip_password pin) +        } ); +      } + +      if ( $svcdb || $svc_phone ) { +        my $part_pkg = $cust_pkg->part_pkg; +	unless ( $part_pkg ) { +	  $dbh->rollback if $oldAutoCommit; +	  return "unknown pkgpart: ". $cust_pkg{'pkgpart'}; +	}  +        if ( $svcdb ) { +          $svc_x{svcpart} = $part_pkg->svcpart_unique_svcdb( $svcdb ); +          my $class = "FS::$svcdb"; +          push @svc_x, $class->new( \%svc_x ); +        } +        if ( $svc_phone ) { +          warn $part_pkg->svcpart_unique_svcdb('svc_phone'); +          $svc_phone->svcpart( $part_pkg->svcpart_unique_svcdb('svc_phone') ); +          push @svc_x, $svc_phone; +        } +      } + +      $hash{$cust_pkg} = \@svc_x; +    } + +    my $error = $cust_main->insert( \%hash, $invoicing_list ); + +    if ( $error ) { +      $dbh->rollback if $oldAutoCommit; +      return "can't insert customer". ( $line ? " for $line" : '' ). ": $error"; +    } + +    if ( $format eq 'simple' ) { + +      #false laziness w/bill.cgi +      $error = $cust_main->bill( 'time' => $billtime ); +      if ( $error ) { +        $dbh->rollback if $oldAutoCommit; +        return "can't bill customer for $line: $error"; +      } +   +      $error = $cust_main->apply_payments_and_credits; +      if ( $error ) { +        $dbh->rollback if $oldAutoCommit; +        return "can't bill customer for $line: $error"; +      } + +      $error = $cust_main->collect(); +      if ( $error ) { +        $dbh->rollback if $oldAutoCommit; +        return "can't collect customer for $line: $error"; +      } + +    } + +    $row++; + +    if ( $job && time - $min_sec > $last ) { #progress bar +      $job->update_statustext( int(100 * $row / $count) ); +      $last = time; +    } + +  } + +  $dbh->commit or die $dbh->errstr if $oldAutoCommit;; + +  return "Empty file!" unless $row; + +  ''; #no error + +} + +=head1 BUGS + +Not enough documentation. + +=head1 SEE ALSO + +L<FS::cust_main>, L<FS::cust_pkg>, +L<FS::svc_acct>, L<FS::svc_external>, L<FS::svc_phone> + +=cut + +1; diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index d8b5e9c69..cab64367d 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -599,15 +599,29 @@ sub svcpart {    my $svcdb = scalar(@_) ? shift : '';    my @svcdb_pkg_svc =      grep { ( $svcdb eq $_->part_svc->svcdb || !$svcdb ) } $self->pkg_svc; -  my @pkg_svc = (); -  @pkg_svc = grep { $_->primary_svc =~ /^Y/i } @svcdb_pkg_svc -    if dbdef->table('pkg_svc')->column('primary_svc'); +  my @pkg_svc = grep { $_->primary_svc =~ /^Y/i } @svcdb_pkg_svc;    @pkg_svc = grep {$_->quantity == 1 } @svcdb_pkg_svc      unless @pkg_svc;    return '' if scalar(@pkg_svc) != 1;    $pkg_svc[0]->svcpart;  } +=item svcpart_unique_svcdb SVCDB + +Returns the svcpart of the a service definition (see L<FS::part_svc>) matching +SVCDB associated with this package definition (see L<FS::pkg_svc>).  Returns +false if there not a primary service definition for SVCDB or there are multiple +service definitions for SVCDB. + +=cut + +sub svcpart_unique_svcdb { +  my( $self, $svcdb ) = @_; +  my @svcdb_pkg_svc = grep { ( $svcdb eq $_->part_svc->svcdb ) } $self->pkg_svc; +  return '' if scalar(@svcdb_pkg_svc) != 1; +  $svcdb_pkg_svc[0]->svcpart; +} +  =item payby  Returns a list of the acceptable payment types for this package.  Eventually diff --git a/FS/MANIFEST b/FS/MANIFEST index 5bae060c1..c3252425b 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -59,6 +59,7 @@ FS/cust_bill_pkg_detail.pm  FS/cust_credit.pm  FS/cust_credit_bill.pm  FS/cust_main.pm +FS/cust_main/Import.pm  FS/cust_main_Mixin.pm  FS/cust_main_county.pm  FS/cust_main_invoice.pm diff --git a/FS/bin/freeside-queued b/FS/bin/freeside-queued index 93d735d1a..815def49d 100644 --- a/FS/bin/freeside-queued +++ b/FS/bin/freeside-queued @@ -164,7 +164,7 @@ while (1) {      #auto-use classes...      #if ( $ljob->job =~ /(FS::part_export::\w+)::/ ) { -    if (    $ljob->job =~ /(FS::part_export::\w+)::/ +    if (    $ljob->job =~ /(FS::(part_export|cust_main)::\w+)::/           || $ljob->job =~ /(FS::\w+)::/         )      { @@ -199,6 +199,9 @@ perl-modules:  	  s|%%%FREESIDE_EXPORT%%%|${FREESIDE_EXPORT}|g;\  	" blib/lib/FS/part_export/*.pm;\  	perl -p -i -e "\ +	  s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\ +	" blib/lib/FS/cust_main/*.pm;\ +	perl -p -i -e "\  	  s|%%%FREESIDE_CONF%%%|${FREESIDE_CONF}|g;\  	  s|%%%FREESIDE_LOG%%%|${FREESIDE_LOG}|g;\  	  s|%%%FREESIDE_LOCK%%%|${FREESIDE_LOCK}|g;\ diff --git a/httemplate/misc/cust_main-import.cgi b/httemplate/misc/cust_main-import.cgi index 066f891ae..b822c5dab 100644 --- a/httemplate/misc/cust_main-import.cgi +++ b/httemplate/misc/cust_main-import.cgi @@ -32,6 +32,7 @@ Import a file containing customer records.          <OPTION VALUE="extended" SELECTED>Extended          <OPTION VALUE="extended-plus_company">Extended plus company          <OPTION VALUE="svc_external">External service +        <OPTION VALUE="svc_external_svc_phone">External service and phone service        </SELECT>      </TD>    </TR> @@ -94,6 +95,9 @@ Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets.  <b>External service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title</i>  <BR><BR> +<b>External service and phone service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title, countrycode, phonenum, sip_password, pin</i> +<BR><BR> +  <%$req%> Required fields  <BR><BR> diff --git a/httemplate/misc/process/cust_main-import.cgi b/httemplate/misc/process/cust_main-import.cgi index df61eb632..2b705a6fc 100644 --- a/httemplate/misc/process/cust_main-import.cgi +++ b/httemplate/misc/process/cust_main-import.cgi @@ -4,6 +4,7 @@  die "access denied"    unless $FS::CurrentUser::CurrentUser->access_right('Import'); -my $server = new FS::UI::Web::JSRPC 'FS::cust_main::process_batch_import', $cgi; +my $server = +  new FS::UI::Web::JSRPC 'FS::cust_main::Import::process_batch_import', $cgi;  </%init> | 
