agent interface
authorivan <ivan>
Thu, 10 Jun 2004 12:31:32 +0000 (12:31 +0000)
committerivan <ivan>
Thu, 10 Jun 2004 12:31:32 +0000 (12:31 +0000)
20 files changed:
FS/FS/CGI.pm
FS/FS/ClientAPI/Agent.pm [new file with mode: 0644]
FS/FS/ClientAPI/MyAccount.pm
FS/FS/ClientAPI/Signup.pm
FS/FS/agent.pm
FS/FS/cust_main.pm
fs_selfservice/FS-SelfService/SelfService.pm
fs_selfservice/FS-SelfService/cgi/agent.cgi [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/agent_login.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/agent_main.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/cvv2.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/cvv2.png [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/cvv2_amex.png [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/list_customers.html [new file with mode: 0644]
fs_selfservice/FS-SelfService/cgi/signup.html [new file with mode: 0755]
fs_selfservice/FS-SelfService/cgi/view_customer.html [new file with mode: 0644]
fs_signup/FS-SignupClient/SignupClient.pm
fs_signup/FS-SignupClient/cgi/signup.cgi
httemplate/browse/agent.cgi
httemplate/search/cust_main.cgi

index a328629..1ddc62c 100644 (file)
@@ -294,6 +294,8 @@ sub small_custview {
     or die "unknown custnum $arg";
 
   my $html = 'Customer #<B>'. $cust_main->custnum. '</B>'.
+    ' - <B><FONT COLOR="'. $cust_main->statuscolor. '">'.
+    ucfirst($cust_main->status). '</FONT></B>'.
     ntable('#e8e8e8'). '<TR><TD>'. ntable("#cccccc",2).
     '<TR><TD ALIGN="right" VALIGN="top">Billing<BR>Address</TD><TD BGCOLOR="#ffffff">'.
     $cust_main->getfield('last'). ', '. $cust_main->first. '<BR>';
@@ -305,6 +307,20 @@ sub small_custview {
   $html .= $cust_main->country. '<BR>'
     if $cust_main->country && $cust_main->country ne $countrydefault;
 
+  $html .= '</TD></TR><TR><TD></TD><TD BGCOLOR="#ffffff">';
+  if ( $cust_main->daytime && $cust_main->night ) {
+    use FS::Msgcat;
+    $html .= ( FS::Msgcat::_gettext('daytime') || 'Day' ).
+             ' '. $cust_main->daytime.
+             '<BR>'. ( FS::Msgcat::_gettext('night') || 'Night' ).
+             ' '. $cust_main->night;
+  } elsif ( $cust_main->daytime || $cust_main->night ) {
+    $html .= $cust_main->daytime || $cust_main->night;
+  }
+  if ( $cust_main->fax ) {
+    $html .= '<BR>Fax '. $cust_main->fax;
+  }
+
   $html .= '</TD></TR></TABLE></TD>';
 
   if ( defined $cust_main->dbdef_table->column('ship_last') ) {
@@ -327,6 +343,23 @@ sub small_custview {
       if $cust_main->get("${pre}country")
          && $cust_main->get("${pre}country") ne $countrydefault;
 
+    $html .= '</TD></TR><TR><TD></TD><TD BGCOLOR="#ffffff">';
+
+    if ( $cust_main->get("${pre}daytime") && $cust_main->get("${pre}night") ) {
+      use FS::Msgcat;
+      $html .= ( FS::Msgcat::_gettext('daytime') || 'Day' ).
+               ' '. $cust_main->get("${pre}daytime").
+               '<BR>'. ( FS::Msgcat::_gettext('night') || 'Night' ).
+               ' '. $cust_main->get("${pre}night");
+    } elsif ( $cust_main->get("${pre}daytime")
+              || $cust_main->get("${pre}night") ) {
+      $html .= $cust_main->get("${pre}daytime")
+               || $cust_main->get("${pre}night");
+    }
+    if ( $cust_main->get("${pre}fax") ) {
+      $html .= '<BR>Fax '. $cust_main->get("${pre}fax");
+    }
+
     $html .= '</TD></TR></TABLE></TD>';
   }
 
diff --git a/FS/FS/ClientAPI/Agent.pm b/FS/FS/ClientAPI/Agent.pm
new file mode 100644 (file)
index 0000000..212faaa
--- /dev/null
@@ -0,0 +1,113 @@
+package FS::ClientAPI::Agent;
+
+#some false laziness w/MyAccount
+
+use strict;
+use vars qw($cache);
+use Digest::MD5 qw(md5_hex);
+use Cache::SharedMemoryCache; #store in db?
+use FS::Record qw(qsearchs); # qsearch);
+use FS::agent;
+
+use FS::ClientAPI;
+FS::ClientAPI->register_handlers(
+  'Agent/agent_login'          => \&agent_login,
+  'Agent/agent_info'           => \&agent_info,
+  'Agent/agent_list_customers' => \&agent_list_customers,
+);
+
+#store in db?
+my $cache = new Cache::SharedMemoryCache( {
+   'namespace' => 'FS::ClientAPI::Agent',
+} );
+
+sub agent_login {
+  my $p = shift;
+
+  #don't allow a blank login to first unconfigured agent with no user/pass
+  return { error => 'Must specify your reseller username and password.' }
+    unless length($p->{'username'}) && length($p->{'password'});
+
+  my $agent = qsearchs( 'agent', {
+    'username'  => $p->{'username'},
+    '_password' => $p->{'password'},
+  } );
+
+  unless ( $agent ) { return { error => 'Incorrect password.' } }
+
+  my $session = { 
+    'agentnum' => $agent->agentnum,
+    'agent'    => $agent->agent,
+  };
+
+  my $session_id;
+  do {
+    $session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
+  } until ( ! defined $cache->get($session_id) ); #just in case
+
+  $cache->set( $session_id, $session, '1 hour' );
+
+  { 'error'      => '',
+    'session_id' => $session_id,
+  };
+}
+
+sub agent_info {
+  my $p = shift;
+
+  my $session = $cache->get($p->{'session_id'})
+    or return { 'error' => "Can't resume session" }; #better error message
+
+  #my %return;
+
+  my $agentnum = $session->{'agentnum'};
+
+  my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
+    or return { 'error' => "unknown agentnum $agentnum" };
+
+  { 'error'        => '',
+    'agentnum'     => $agentnum,
+    'agent'        => $agent->agent,
+    'num_prospect' => $agent->num_prospect_cust_main,
+    'num_active'   => $agent->num_active_cust_main,
+    'num_susp'     => $agent->num_susp_cust_main,
+    'num_cancel'   => $agent->num_cancel_cust_main,
+    #%return,
+  };
+
+}
+
+sub agent_list_customers {
+  my $p = shift;
+
+  my $session = $cache->get($p->{'session_id'})
+    or return { 'error' => "Can't resume session" }; #better error message
+
+  #my %return;
+
+  my $agentnum = $session->{'agentnum'};
+
+  my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
+    or return { 'error' => "unknown agentnum $agentnum" };
+
+  my @cust_main = ();
+
+  warn $p->{'susp'};
+
+  push @cust_main,
+    map $agent->$_(), map $_.'_cust_main',
+      grep $p->{$_}, qw( prospect active susp cancel );
+
+  { customers => [ map {
+                         my $cust_main = $_;
+                         my $hashref = $cust_main->hashref;
+                         $hashref->{$_} = $cust_main->$_()
+                           foreach qw(name status statuscolor);
+                         delete $hashref->{$_} foreach qw( payinfo paycvv );
+                         $hashref;
+                   } @cust_main
+                 ],
+  }
+
+}
+
index 77c1fc8..271d0c2 100644 (file)
@@ -25,6 +25,7 @@ FS::ClientAPI->register_handlers(
   'MyAccount/customer_info'    => \&customer_info,
   'MyAccount/edit_info'        => \&edit_info,
   'MyAccount/invoice'          => \&invoice,
+  'MyAccount/list_invoices'    => \&list_invoices,
   'MyAccount/cancel'           => \&cancel,
   'MyAccount/payment_info'     => \&payment_info,
   'MyAccount/process_payment'  => \&process_payment,
@@ -86,16 +87,31 @@ sub login {
 
 sub customer_info {
   my $p = shift;
-  my $session = $cache->get($p->{'session_id'})
-    or return { 'error' => "Can't resume session" }; #better error message
-
-  my %return;
 
-  my $custnum = $session->{'custnum'};
+  my($session, $custnum, $context);
+  if ( $p->{'session_id'} ) {
+    $context = 'customer';
+    $session = $cache->get($p->{'session_id'})
+      or return { 'error' => "Can't resume session" }; #better error message
+    $custnum = $session->{'custnum'};
+  } elsif ( $p->{'agent_session_id'} ) {
+    $context = 'agent';
+    my $agent_cache = new Cache::SharedMemoryCache( {
+      'namespace' => 'FS::ClientAPI::Agent',
+    } );
+    $session = $agent_cache->get($p->{'agent_session_id'})
+      or return { 'error' => "Can't resume session" }; #better error message
+    $custnum = $p->{'custnum'};
+  } else {
+    return { 'error' => "Can't resume session" }; #better error message
+  }
 
+  my %return;
   if ( $custnum ) { #customer record
 
-    my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+    my $search = { 'custnum' => $custnum };
+    $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+    my $cust_main = qsearchs('cust_main', $search )
       or return { 'error' => "unknown custnum $custnum" };
 
     $return{balance} = $cust_main->balance;
@@ -357,6 +373,27 @@ sub invoice {
 
 }
 
+sub list_invoices {
+  my $p = shift;
+  my $session = $cache->get($p->{'session_id'})
+    or return { 'error' => "Can't resume session" }; #better error message
+
+  my $custnum = $session->{'custnum'};
+
+  my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+    or return { 'error' => "unknown custnum $custnum" };
+
+  my @cust_bill = $cust_main->cust_bill;
+
+  return  { 'error'       => '',
+            'invoices'    =>  [ map { { 'invnum' => $_->invnum,
+                                        '_date'  => $_->_date,
+                                      }
+                                    } @cust_bill
+                              ]
+          };
+}
+
 sub cancel {
   my $p = shift;
   my $session = $cache->get($p->{'session_id'})
@@ -391,12 +428,30 @@ sub list_pkgs {
 
 sub order_pkg {
   my $p = shift;
-  my $session = $cache->get($p->{'session_id'})
-    or return { 'error' => "Can't resume session" }; #better error message
 
-  my $custnum = $session->{'custnum'};
+  my($session, $custnum, $context);
+
+  if ( $p->{'session_id'} ) {
+    $context = 'customer';
+    $session = $cache->get($p->{'session_id'})
+      or return { 'error' => "Can't resume session" }; #better error message
+    $custnum = $session->{'custnum'};
+  } elsif ( $p->{'agent_session_id'} ) {
+    $context = 'agent';
+    my $agent_cache = new Cache::SharedMemoryCache( {
+      'namespace' => 'FS::ClientAPI::Agent',
+    } );
+    $session = $agent_cache->get($p->{'agent_session_id'})
+      or return { 'error' => "Can't resume session" }; #better error message
+    $custnum = $p->{'custnum'};
+  } else {
+    return { 'error' => "Can't resume session" }; #better error message
+  }
 
-  my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+  my $search = { 'custnum' => $custnum };
+  $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+
+  my $cust_main = qsearchs('cust_main', $search )
     or return { 'error' => "unknown custnum $custnum" };
 
   #false laziness w/ClientAPI/Signup.pm
@@ -503,13 +558,13 @@ sub cancel_pkg {
   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
     or return { 'error' => "unknown custnum $custnum" };
 
-  my $pkgnum = $session->{'pkgnum'};
+  my $pkgnum = $p->{'pkgnum'};
 
   my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
                                         'pkgnum'  => $pkgnum,   } )
     or return { 'error' => "unknown pkgnum $pkgnum" };
 
-  my $error = $cust_main->cancel( 'quiet'=>1 );
+  my $error = $cust_pkg->cancel( 'quiet'=>1 );
   return { 'error' => $error };
 
 }
index 4655b09..2e2b80f 100644 (file)
@@ -22,7 +22,7 @@ FS::ClientAPI->register_handlers(
 );
 
 sub signup_info {
-  #my $packet = shift;
+  my $packet = shift;
 
   my $conf = new FS::Conf;
 
@@ -87,24 +87,35 @@ sub signup_info {
 
   };
 
-  if (
-    $conf->config('signup_server-default_agentnum')
-    && !exists $signup_info->{'part_pkg'} #cache for performance
-  ) {
-    my $agentnum = $conf->config('signup_server-default_agentnum');
-    my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
-      or die "fatal: signup_server-default_agentnum $agentnum not found\n";
-    my $pkgpart_href = $agent->pkgpart_hashref;
-
-    $signup_info->{'part_pkg'} = [
-      #map { $_->hashref }
-      map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
-        grep { $_->svcpart('svc_acct') && $pkgpart_href->{ $_->pkgpart } }
-          qsearch( 'part_pkg', { 'disabled' => '' } )
-    ];
+  my $agentnum = $conf->config('signup_server-default_agentnum');
+
+  my $session = '';
+  if ( exists $packet->{'session_id'} ) {
+    my $cache = new Cache::SharedMemoryCache( {
+      'namespace' => 'FS::ClientAPI::Agent',
+    } );
+    $session = $cache->get($packet->{'session_id'});
+    if ( $session ) {
+      $agentnum = $session->{'agentnum'};
+    } else {
+      return { 'error' => "Can't resume session" }; #better error message
+    }
   }
 
-  $signup_info;
+  if ( $agentnum ) {
+    $signup_info->{'part_pkg'} = $signup_info->{'agentnum2part_pkg'}{$agentnum};
+  } else {
+    delete $signup_info->{'part_pkg'};
+  }
+
+  if ( $session ) {
+    my $agent_signup_info = { %$signup_info };
+    delete $agent_signup_info->{agentnum2part_pkg};
+    $agent_signup_info->{'agent'} = $session->{'agent'};
+    $agent_signup_info;
+  } else {
+    $signup_info;
+  }
 
 }
 
@@ -122,12 +133,27 @@ sub new_customer {
   return { 'error' => gettext('no_access_number_selected') }
     unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} ));
 
+  my $agentnum;
+  if ( exists $packet->{'session_id'} ) {
+    my $cache = new Cache::SharedMemoryCache( {
+      'namespace' => 'FS::ClientAPI::Agent',
+    } );
+    my $session = $cache->get($packet->{'session_id'});
+    if ( $session ) {
+      $agentnum = $session->{'agentnum'};
+    } else {
+      return { 'error' => "Can't resume session" }; #better error message
+    }
+  } else {
+    $agentnum = $packet->{agentnum}
+                || $conf->config('signup_server-default_agentnum');
+  }
+
   #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
   # common that are still here and library them.
   my $cust_main = new FS::cust_main ( {
     #'custnum'          => '',
-    'agentnum'      => $packet->{agentnum}
-                       || $conf->config('signup_server-default_agentnum'),
+    'agentnum'      => $agentnum,
     'refnum'        => $packet->{refnum}
                        || $conf->config('signup_server-default_refnum'),
 
index 2f70d65..7c69dfb 100644 (file)
@@ -2,7 +2,7 @@ package FS::agent;
 
 use strict;
 use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( dbh qsearch qsearchs );
 use FS::cust_main;
 use FS::agent_type;
 
@@ -164,11 +164,107 @@ sub pkgpart_hashref {
   $self->agent_type->pkgpart_hashref;
 }
 
-=back
+=item num_prospect_cust_main
+
+Returns the number of prospects (customers with no packages ever ordered) for
+this agent.
+
+=cut
+
+sub num_prospect_cust_main {
+  shift->num_sql(FS::cust_main->prospect_sql);
+}
+
+sub num_sql {
+  my( $self, $sql ) = @_;
+  my $sth = dbh->prepare(
+    "SELECT COUNT(*) FROM cust_main WHERE agentnum = ? AND $sql"
+  ) or die dbh->errstr;
+  $sth->execute($self->agentnum) or die $sth->errstr;
+  $sth->fetchrow_arrayref->[0];
+}
+
+=item prospect_cust_main
+
+Returns the prospects (customers with no packages ever ordered) for this agent,
+as cust_main objects.
+
+=cut
+
+sub prospect_cust_main {
+  shift->cust_main_sql(FS::cust_main->prospect_sql);
+}
+
+sub cust_main_sql {
+  my( $self, $sql ) = @_;
+  qsearch( 'cust_main',
+           { 'agentnum' => $self->agentnum },
+           '',
+           " AND $sql"
+  );
+}
+
+=item num_active_cust_main
+
+Returns the number of active customers for this agent.
+
+=cut
+
+sub num_active_cust_main {
+  shift->num_sql(FS::cust_main->active_sql);
+}
+
+=item active_cust_main
+
+Returns the active customers for this agent, as cust_main objects.
+
+=cut
 
-=head1 VERSION
+sub active_cust_main {
+  shift->cust_main_sql(FS::cust_main->active_sql);
+}
+
+=item num_susp_cust_main
+
+Returns the number of suspended customers for this agent.
+
+=cut
+
+sub num_susp_cust_main {
+  shift->num_sql(FS::cust_main->susp_sql);
+}
+
+=item susp_cust_main
+
+Returns the suspended customers for this agent, as cust_main objects.
+
+=cut
+
+sub susp_cust_main {
+  shift->cust_main_sql(FS::cust_main->susp_sql);
+}
 
-$Id: agent.pm,v 1.6 2003-09-30 15:01:46 ivan Exp $
+=item num_cancel_cust_main
+
+Returns the number of cancelled customer for this agent.
+
+=cut
+
+sub num_cancel_cust_main {
+  shift->num_sql(FS::cust_main->cancel_sql);
+}
+
+=item cancel_cust_main
+
+Returns the cancelled customers for this agent, as cust_main objects.
+
+=cut
+
+sub cancel_cust_main {
+  shift->cust_main_sql(FS::cust_main->cancel_sql);
+}
+
+=back
 
 =head1 BUGS
 
index fbd4618..07f0a34 100644 (file)
@@ -2538,6 +2538,136 @@ sub select_for_update {
   qsearch('cust_main', { 'custnum' => $self->custnum }, '*', 'FOR UPDATE' );
 }
 
+=item name
+
+Returns a name string for this customer, either "Company (Last, First)" or
+"Last, First".
+
+=cut
+
+sub name {
+  my $self = shift;
+  my $name = $self->get('last'). ', '. $self->first;
+  $name = $self->company. " ($name)" if $self->company;
+  $name;
+}
+
+=item status
+
+Returns a status string for this customer, currently:
+
+=over 4
+
+=item prospect - No packages have ever been ordered
+
+=item active - One or more recurring packages is active
+
+=item suspended - All non-cancelled recurring packages are suspended
+
+=item cancelled - All recurring packages are cancelled
+
+=back
+
+=cut
+
+sub status {
+  my $self = shift;
+  for my $status (qw( prospect active suspended cancelled )) {
+    my $method = $status.'_sql';
+    my $numnum = ( my $sql = $self->$method() ) =~ s/cust_main\.custnum/?/g;
+    my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
+    $sth->execute( ($self->custnum) x $numnum ) or die $sth->errstr;
+    return $status if $sth->fetchrow_arrayref->[0];
+  }
+}
+
+=item statuscolor
+
+Returns a hex triplet color string for this customer's status.
+
+=cut
+
+my %statuscolor = (
+  'prospect'  => '000000',
+  'active'    => '00CC00',
+  'suspended' => 'FF9900',
+  'cancelled' => 'FF0000',
+);
+sub statuscolor {
+  my $self = shift;
+  $statuscolor{$self->status};
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item prospect_sql
+
+Returns an SQL expression identifying prospective cust_main records (customers
+with no packages ever ordered)
+
+=cut
+
+sub prospect_sql { "
+  0 = ( SELECT COUNT(*) FROM cust_pkg
+          WHERE cust_pkg.custnum = cust_main.custnum
+      )
+"; }
+
+=item active_sql
+
+Returns an SQL expression identifying active cust_main records.
+
+=cut
+
+sub active_sql { "
+  0 < ( SELECT COUNT(*) FROM cust_pkg
+          WHERE cust_pkg.custnum = cust_main.custnum
+            AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+            AND ( cust_pkg.susp   IS NULL OR cust_pkg.susp   = 0 )
+      )
+"; }
+
+=item susp_sql
+=item suspended_sql
+
+Returns an SQL expression identifying suspended cust_main records.
+
+=cut
+
+sub suspended_sql { susp_sql(@_); }
+sub susp_sql { "
+    0 < ( SELECT COUNT(*) FROM cust_pkg
+            WHERE cust_pkg.custnum = cust_main.custnum
+              AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+        )
+    AND 0 = ( SELECT COUNT(*) FROM cust_pkg
+                WHERE cust_pkg.custnum = cust_main.custnum
+                  AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
+            )
+"; }
+
+=item cancel_sql
+=item cancelled_sql
+
+Returns an SQL expression identifying cancelled cust_main records.
+
+=cut
+
+sub cancelled_sql { cancel_sql(@_); }
+sub cancel_sql { "
+  0 < ( SELECT COUNT(*) FROM cust_pkg
+          WHERE cust_pkg.custnum = cust_main.custnum
+      )
+  AND 0 = ( SELECT COUNT(*) FROM cust_pkg
+              WHERE cust_pkg.custnum = cust_main.custnum
+                AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+          )
+"; }
+
 =back
 
 =head1 SUBROUTINES
index 920415e..2cda9fe 100644 (file)
@@ -18,24 +18,28 @@ $socket .= '.'.$tag if defined $tag && length($tag);
 
 #maybe should ask ClientAPI for this list
 %autoload = (
-  'passwd'          => 'passwd/passwd',
-  'chfn'            => 'passwd/passwd',
-  'chsh'            => 'passwd/passwd',
-  'login'           => 'MyAccount/login',
-  'customer_info'   => 'MyAccount/customer_info',
-  'edit_info'       => 'MyAccount/edit_info',
-  'invoice'         => 'MyAccount/invoice',
-  'cancel'          => 'MyAccount/cancel',
-  'payment_info'    => 'MyAccount/payment_info',
-  'process_payment' => 'MyAccount/process_payment',
-  'list_pkgs'       => 'MyAccount/list_pkgs',
-  'order_pkg'       => 'MyAccount/order_pkg',
-  'cancel_pkg'      => 'MyAccount/cancel_pkg',
-  'charge'          => 'MyAccount/charge',
-  'signup_info'     => 'Signup/signup_info',
-  'new_customer'    => 'Signup/new_customer',
+  'passwd'               => 'passwd/passwd',
+  'chfn'                 => 'passwd/passwd',
+  'chsh'                 => 'passwd/passwd',
+  'login'                => 'MyAccount/login',
+  'customer_info'        => 'MyAccount/customer_info',
+  'edit_info'            => 'MyAccount/edit_info',
+  'invoice'              => 'MyAccount/invoice',
+  'list_invoices'        => 'MyAccount/list_invoices',
+  'cancel'               => 'MyAccount/cancel',
+  'payment_info'         => 'MyAccount/payment_info',
+  'process_payment'      => 'MyAccount/process_payment',
+  'list_pkgs'            => 'MyAccount/list_pkgs',
+  'order_pkg'            => 'MyAccount/order_pkg',
+  'cancel_pkg'           => 'MyAccount/cancel_pkg',
+  'charge'               => 'MyAccount/charge',
+  'signup_info'          => 'Signup/signup_info',
+  'new_customer'         => 'Signup/new_customer',
+  'agent_login'          => 'Agent/agent_login',
+  'agent_info'           => 'Agent/agent_info',
+  'agent_list_customers' => 'Agent/agent_list_customers',
 );
-@EXPORT_OK = keys %autoload;
+@EXPORT_OK = ( keys(%autoload), qw( regionselector expselect popselector ) );
 
 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
 $ENV{'SHELL'} = '/bin/sh';
@@ -286,6 +290,37 @@ Invoice text
 
 =back
 
+=item list_invoices HASHREF
+
+Returns a list of all customer invoices.  Takes a hash references with a single
+key, session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invoices
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item invnum
+
+Invoice ID
+
+=item _date
+
+Invoice date, in UNIX epoch time
+
+=back
+
+=back
+
 =item cancel HASHREF
 
 Cancels this customer.
@@ -492,7 +527,15 @@ error message on errors.
 
 =over 4
 
-=item signup_info
+=item signup_info HASHREF
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id - Optional agent/reseller interface session
+
+=back
 
 Returns a hash reference containing information that may be useful in
 displaying a signup page.  The hash reference contains the following keys:
@@ -505,7 +548,9 @@ County/state/country data - array reference of hash references, each of which ha
 
 =item part_pkg
 
-Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>).  Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>).  Note these are not FS::part_pkg objects, but hash references of columns and values.  Requires the 'signup_server-default_agentnum' configuration value to be set.
+Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>).  Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>).  Note these are not FS::part_pkg objects, but hash references of columns and values.  Requires the 'signup_server-default_agentnum' configuration value to be set, or
+an agentnum specified explicitly via reseller interface session_id in the
+options.
 
 =item agent
 
@@ -634,6 +679,369 @@ Returns a hash reference with the following keys:
 
 =back
 
+=item regionselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item selected_county
+
+=item selected_state
+
+=item selected_country
+
+=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
+
+=item onchange - Specify a javascript subroutine to call on changes
+
+=item default_state
+
+=item default_country
+
+=item locales - An arrayref of hash references specifying regions.  Normally you can just pass the value of the I<cust_main_county> field returned by B<signup_info>.
+
+=back
+
+Returns a list consisting of three HTML fragments for county selection,
+state selection and country selection, respectively.
+
+=cut
+
+#false laziness w/FS::cust_main_county (this is currently the "newest" version)
+sub regionselector {
+  my $param;
+  if ( ref($_[0]) ) {
+    $param = shift;
+  } else {
+    $param = { @_ };
+  }
+  $param->{'selected_country'} ||= $param->{'default_country'};
+  $param->{'selected_state'} ||= $param->{'default_state'};
+
+  my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+
+  my $countyflag = 0;
+
+  my %cust_main_county;
+
+#  unless ( @cust_main_county ) { #cache 
+    #@cust_main_county = qsearch('cust_main_county', {} );
+    #foreach my $c ( @cust_main_county ) {
+    foreach my $c ( @{ $param->{'locales'} } ) {
+      #$countyflag=1 if $c->county;
+      $countyflag=1 if $c->{county};
+      #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
+      #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
+      $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
+    }
+#  }
+  $countyflag=1 if $param->{selected_county};
+
+  my $script_html = <<END;
+    <SCRIPT>
+    function opt(what,value,text) {
+      var optionName = new Option(text, value, false, false);
+      var length = what.length;
+      what.options[length] = optionName;
+    }
+    function ${prefix}country_changed(what) {
+      country = what.options[what.selectedIndex].text;
+      for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
+          what.form.${prefix}state.options[i] = null;
+END
+      #what.form.${prefix}state.options[0] = new Option('', '', false, true);
+
+  foreach my $country ( sort keys %cust_main_county ) {
+    $script_html .= "\nif ( country == \"$country\" ) {\n";
+    foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+      my $text = $state || '(n/a)';
+      $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
+    }
+    $script_html .= "}\n";
+  }
+
+  $script_html .= <<END;
+    }
+    function ${prefix}state_changed(what) {
+END
+
+  if ( $countyflag ) {
+    $script_html .= <<END;
+      state = what.options[what.selectedIndex].text;
+      country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+      for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
+          what.form.${prefix}county.options[i] = null;
+END
+
+    foreach my $country ( sort keys %cust_main_county ) {
+      $script_html .= "\nif ( country == \"$country\" ) {\n";
+      foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+        $script_html .= "\nif ( state == \"$state\" ) {\n";
+          #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+          foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+            my $text = $county || '(n/a)';
+            $script_html .=
+              qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+          }
+        $script_html .= "}\n";
+      }
+      $script_html .= "}\n";
+    }
+  }
+
+  $script_html .= <<END;
+    }
+    </SCRIPT>
+END
+
+  my $county_html = $script_html;
+  if ( $countyflag ) {
+    $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
+    $county_html .= '</SELECT>';
+  } else {
+    $county_html .=
+      qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
+  }
+
+  my $state_html = qq!<SELECT NAME="${prefix}state" !.
+                   qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
+  foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
+    my $text = $state || '(n/a)';
+    my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
+    $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
+  }
+  $state_html .= '</SELECT>';
+
+  $state_html .= '</SELECT>';
+
+  my $country_html = qq!<SELECT NAME="${prefix}country" !.
+                     qq!onChange="${prefix}country_changed(this); $param->{'onchange'}">!;
+  my $countrydefault = $param->{default_country} || 'US';
+  foreach my $country (
+    sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
+      keys %cust_main_county
+  ) {
+    my $selected = $country eq $param->{'selected_country'} ? ' SELECTED' : '';
+    $country_html .= "\n<OPTION$selected>$country</OPTION>"
+  }
+  $country_html .= '</SELECT>';
+
+  ($county_html, $state_html, $country_html);
+
+}
+
+#=item expselect HASHREF | LIST
+#
+#Takes as input a hashref or list of key/value pairs with the following keys:
+#
+#=over 4
+#
+#=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
+#
+#=item date - current date, in yyyy-mm-dd or m-d-yyyy format
+#
+#=back
+
+=item expselect PREFIX [ DATE ]
+
+Takes as input a unique prefix string and the current expiration date, in
+yyyy-mm-dd or m-d-yyyy format
+
+Returns an HTML fragments for expiration date selection.
+
+=cut
+
+sub expselect {
+  #my $param;
+  #if ( ref($_[0]) ) {
+  #  $param = shift;
+  #} else {
+  #  $param = { @_ };
+  #my $prefix = $param->{'prefix'};
+  #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+  #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
+  my $prefix = shift;
+  my $date = scalar(@_) ? shift : '';
+
+  my( $m, $y ) = ( 0, 0 );
+  if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
+    ( $m, $y ) = ( $2, $1 );
+  } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+    ( $m, $y ) = ( $1, $3 );
+  }
+  my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
+  for ( 1 .. 12 ) {
+    $return .= "<OPTION";
+    $return .= " SELECTED" if $_ == $m;
+    $return .= ">$_";
+  }
+  $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
+  my @t = localtime;
+  my $thisYear = $t[5] + 1900;
+  for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. 2037 ) {
+    $return .= "<OPTION";
+    $return .= " SELECTED" if $_ == $y;
+    $return .= ">$_";
+  }
+  $return .= "</SELECT>";
+
+  $return;
+}
+
+=item popselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item popnum
+
+=item pops - An arrayref of hash references specifying access numbers.  Normally you can just pass the value of the I<svc_acct_pop> field returned by B<signup_info>.
+
+=back
+
+Returns an HTML fragment for access number selection.
+
+=cut
+
+#horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
+sub popselector {
+  my $param;
+  if ( ref($_[0]) ) {
+    $param = shift;
+  } else {
+    $param = { @_ };
+  }
+  my $popnum = $param->{'popnum'};
+  my $pops = $param->{'pops'};
+
+  return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
+  return $pops->[0]{city}. ', '. $pops->[0]{state}.
+         ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
+         '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
+    if scalar(@$pops) == 1;
+
+  my %pop = ();
+  my %popnum2pop = ();
+  foreach (@$pops) {
+    push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
+    $popnum2pop{$_->{popnum}} = $_;
+  }
+
+  my $text = <<END;
+    <SCRIPT>
+    function opt(what,href,text) {
+      var optionName = new Option(text, href, false, false)
+      var length = what.length;
+      what.options[length] = optionName;
+    }
+END
+
+  my $init_popstate = $param->{'init_popstate'};
+  if ( $init_popstate ) {
+    $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
+             $init_popstate. '">';
+  } else {
+    $text .= <<END;
+      function acstate_changed(what) {
+        state = what.options[what.selectedIndex].text;
+        what.form.popac.options.length = 0
+        what.form.popac.options[0] = new Option("Area code", "-1", false, true);
+END
+  } 
+
+  my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
+  foreach my $state ( sort { $a cmp $b } @states ) {
+    $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
+
+    foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
+      $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
+      if ($ac eq $param->{'popac'}) {
+        $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
+      }
+    }
+    $text .= "}\n" unless $init_popstate;
+  }
+  $text .= "popac_changed(what.form.popac)}\n";
+
+  $text .= <<END;
+  function popac_changed(what) {
+    ac = what.options[what.selectedIndex].text;
+    what.form.popnum.options.length = 0;
+    what.form.popnum.options[0] = new Option("City", "-1", false, true);
+
+END
+
+  foreach my $state ( @states ) {
+    foreach my $popac ( keys %{ $pop{$state} } ) {
+      $text .= "\nif ( ac == \"$popac\" ) {\n";
+
+      foreach my $pop ( @{$pop{$state}->{$popac}}) {
+        my $o_popnum = $pop->{popnum};
+        my $poptext =  $pop->{city}. ', '. $pop->{state}.
+                       ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+
+        $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
+        if ($popnum == $o_popnum) {
+          $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
+        }
+      }
+      $text .= "}\n";
+    }
+  }
+
+
+  $text .= "}\n</SCRIPT>\n";
+
+  $text .=
+    qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
+    qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
+  $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
+           ">$_" foreach sort { $a cmp $b } @states;
+  $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
+
+  $text .=
+    qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
+    qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
+
+  $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
+
+
+  #comment this block to disable initial list polulation
+  my @initial_select = ();
+  if ( scalar( @$pops ) > 100 ) {
+    push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
+  } else {
+    @initial_select = @$pops;
+  }
+  foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
+    $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
+             ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
+             $pop->{city}. ', '. $pop->{state}.
+               ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+  }
+
+  $text .= qq!</SELECT></TD></TR></TABLE>!;
+
+  $text;
+
+}
+
+=back
+
+=head1 RESELLER FUNCTIONS
+
+Note: Resellers can also use the B<signup_info> and B<new_customer> functions
+with their active session.
+
+=over 4
+
+=item agent_login
+
+=item agent_info
+
+=item agent_list_customers
 
 =back
 
diff --git a/fs_selfservice/FS-SelfService/cgi/agent.cgi b/fs_selfservice/FS-SelfService/cgi/agent.cgi
new file mode 100644 (file)
index 0000000..3508e82
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/perl -Tw
+
+#some false laziness w/selfservice.cgi
+
+use strict;
+use vars qw($cgi $session_id $form_max $template_dir);
+use subs qw(do_template);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+use Business::CreditCard;
+use Text::Template;
+use FS::SelfService qw( agent_login agent_info
+                        agent_list_customers
+                        signup_info new_customer
+                        customer_info order_pkg
+                      );
+
+$template_dir = '.';
+
+$form_max = 255;
+
+$cgi = new CGI;
+
+unless ( defined $cgi->param('session') ) {
+  do_template('agent_login',{});
+  exit;
+}
+
+if ( $cgi->param('session') eq 'login' ) {
+
+  $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
+    or die "illegal username";
+  my $username = $1;
+
+  $cgi->param('password') =~ /^(.{0,$form_max})$/
+    or die "illegal password";
+  my $password = $1;
+
+  my $rv = agent_login(
+    'username' => $username,
+    'password' => $password,
+  );
+  if ( $rv->{error} ) {
+    do_template('agent_login', {
+      'error'    => $rv->{error},
+      'username' => $username,
+    } );
+    exit;
+  } else {
+    $cgi->param('session' => $rv->{session_id} );
+    $cgi->param('action'  => 'agent_main' );
+  }
+}
+
+$session_id = $cgi->param('session');
+
+$cgi->param('action') =~
+   /^(agent_main|signup|process_signup|list_customers|view_customer|process_order_pkg)$/
+  or die "unknown action ". $cgi->param('action');
+my $action = $1;
+
+warn "running $action\n";
+my $result = eval "&$action();";
+die $@ if $@;
+
+if ( $result->{error} eq "Can't resume session" ) { #ick
+  do_template('agent_login',{});
+  exit;
+}
+
+warn "processing template $action\n";
+do_template($action, {
+  'session_id' => $session_id,
+  %{$result}
+});
+
+#-- 
+
+sub agent_main { agent_info( 'session_id' => $session_id ); }
+
+sub signup { signup_info( 'session_id' => $session_id ); }
+
+sub process_signup {
+
+  my $init_data = signup_info( 'session_id' => $session_id );
+  if ( $init_data->{'error'} ) {
+    if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+      do_template('agent_login',{});
+      exit;
+    } else { #?
+      die $init_data->{'error'};
+    }
+  }
+
+  my $error = '';
+
+  #some false laziness w/signup.cgi
+  my $payby = $cgi->param('payby');
+  if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+    #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
+    $cgi->param('payinfo' => $cgi->param($payby. '_payinfo1'). '@'. 
+                             $cgi->param($payby. '_payinfo2')
+               );
+  } else {
+    $cgi->param('payinfo' => $cgi->param( $payby. '_payinfo' ) );
+  }
+  $cgi->param('paydate' => $cgi->param( $payby. '_month' ). '-'.
+                           $cgi->param( $payby. '_year' )
+             );
+  $cgi->param('payname' => $cgi->param( $payby. '_payname' ) );
+  $cgi->param('paycvv' => defined $cgi->param( $payby. '_paycvv' )
+                            ? $cgi->param( $payby. '_paycvv' )
+                            : ''
+             );
+
+  if ( $cgi->param('invoicing_list') ) {
+    $cgi->param('invoicing_list' => $cgi->param('invoicing_list'). ', POST')
+      if $cgi->param('invoicing_list_POST');
+  } else {
+    $cgi->param('invoicing_list' => 'POST' );
+  }
+
+  if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+    $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
+    $cgi->param('_password', '');
+    $cgi->param('_password2', '');
+  }
+
+  if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
+    my $payinfo = $cgi->param('payinfo');
+    $payinfo =~ s/\D//g;
+
+    $payinfo =~ /^(\d{13,16})$/
+      or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+    $payinfo = $1;
+    validate($payinfo)
+      or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+    cardtype($payinfo) eq $cgi->param('CARD_type')
+      or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+  }
+
+  unless ( $error ) {
+    my $rv = new_customer ( {
+      'session_id'       => $session_id,
+      map { $_ => $cgi->param($_) }
+        qw( last first ss company
+            address1 address2 city county state zip country
+            daytime night fax
+            payby payinfo paycvv paydate payname invoicing_list
+            pkgpart username sec_phrase _password popnum refnum
+          ),
+        grep { /^snarf_/ } $cgi->param
+    } );
+    $error = $rv->{'error'};
+  }
+
+  if ( $error ) { 
+    $action = 'signup';
+    my $r = { 
+      $cgi->Vars,
+      %{$init_data},
+      'error' => $error,
+    };
+    #warn join('\n', map "$_ => $r->{$_}", keys %$r )."\n";
+    $r;
+  } else {
+    $action = 'agent_main';
+    my $agent_info = agent_info( 'session_id' => $session_id );
+    $agent_info->{'message'} = 'Signup sucessful';
+    $agent_info;
+  }
+
+}
+
+sub list_customers {
+  agent_list_customers( 'session_id' => $session_id,
+                        map { $_ => $cgi->param($_) }
+                          grep defined($cgi->param($_)),
+                               qw(prospect active susp cancel)
+                      );
+}
+
+sub view_customer {
+
+  my $init_data = signup_info( 'session_id' => $session_id );
+  if ( $init_data->{'error'} ) {
+    if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+      do_template('agent_login',{});
+      exit;
+    } else { #?
+      die $init_data->{'error'};
+    }
+  }
+
+  my $customer_info = customer_info (
+    'agent_session_id' => $session_id,
+    'custnum'          => $cgi->param('custnum')
+  );
+
+
+  return {
+    ( map { $_ => $init_data->{$_} }
+          qw( part_pkg security_phrase svc_acct_pop ),
+    ),
+    %$customer_info,
+  };
+}
+
+sub process_order_pkg {
+
+  my $results = order_pkg (
+    'agent_session_id' => $session_id,
+    map { $_ => $cgi->param($_) }
+        qw( custnum pkgpart username _password _password2 sec_phrase popnum )
+  );
+
+  $action = 'view_customer';
+  $cgi->delete( grep { $_ ne 'custnum' } $cgi->param )
+    unless $results->{'error'};
+
+  return {
+    $cgi->Vars,
+    %{view_customer()},
+    'message' => $results->{'error'}
+                   ? '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>'
+                   : 'Package order sucessful.'
+  };
+
+}
+
+#--
+
+sub do_template {
+  my $name = shift;
+  my $fill_in = shift;
+  #warn join(' / ', map { "$_=>".$fill_in->{$_} } keys %$fill_in). "\n";
+
+  $cgi->delete_all();
+  $fill_in->{'selfurl'} = $cgi->self_url;
+  $fill_in->{'cgi'} = \$cgi;
+
+  my $template = new Text::Template( TYPE    => 'FILE',
+                                     SOURCE  => "$template_dir/$name.html",
+                                     DELIMITERS => [ '<%=', '%>' ],
+                                     UNTAINT => 1,                    )
+    or die $Text::Template::ERROR;
+
+  print $cgi->header( '-expires' => 'now' ),
+        $template->fill_in( PACKAGE => 'FS::SelfService::_agentcgi',
+                            HASH    => $fill_in
+                          );
+}
+
+package FS::SelfService::_agentcgi;
+use FS::SelfService qw(regionselector expselect popselector);
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_login.html b/fs_selfservice/FS-SelfService/cgi/agent_login.html
new file mode 100644 (file)
index 0000000..4b0778e
--- /dev/null
@@ -0,0 +1,22 @@
+<HTML><HEAD><TITLE>Reseller Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Reseller Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+<TR>
+  <TH ALIGN="right">Username </TH>
+  <TD>
+    <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>">
+  </TD>
+</TR>
+<TR>
+  <TH ALIGN="right">Password </TH>
+  <TD>
+    <INPUT TYPE="password" NAME="password">
+  </TD>
+</TR>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Login">
+</FORM></BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_main.html b/fs_selfservice/FS-SelfService/cgi/agent_main.html
new file mode 100644 (file)
index 0000000..89a1b33
--- /dev/null
@@ -0,0 +1,40 @@
+<HTML><HEAD><TITLE>Reseller Main</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller Main</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR><TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+<A HREF="<%= $url %>agent_main">Reseller Main</A><BR>
+<!-- <A HREF="<%= $url %>other">SomethingElse</A><BR> -->
+</TD><TD VALIGN="top">
+
+<%= $message
+      ? "<FONT SIZE=\"+2\"><B>$message</B></FONT>"
+      : "Hello $agent!"
+%><BR><BR>
+
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">
+<TR><TH BGCOLOR="#cccccc">Customer summary</TH></TR>
+<TR><TD BGCOLOR="#dddddd">
+
+  <B><%= $num_prospect %></B>
+  <A HREF="<%= $url %>list_customers&prospect=1">prospects</A>
+
+  <BR><FONT COLOR="#00CC00"><B><%= $num_active %></B></FONT>
+  <A HREF="<%= $url %>list_customers&active=1">active</A>
+
+  <BR><FONT COLOR="#FF9900"><B><%= $num_susp %></B></FONT>
+  <A HREF="<%= $url %>list_customers&susp=1">suspended</A>
+
+  <BR><FONT COLOR="#FF0000"><B><%= $num_cancel %></B></FONT>
+  <A HREF="<%= $url %>list_customers&cancel=1">cancelled</A>
+
+</TD></TR></TABLE>
+
+<BR><A HREF="<%= $url %>signup">New customer<!--/prospect--></A>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.html b/fs_selfservice/FS-SelfService/cgi/cvv2.html
new file mode 100644 (file)
index 0000000..b178c85
--- /dev/null
@@ -0,0 +1,25 @@
+<HTML>
+  <HEAD>
+    <TITLE>
+      CVV2 information
+    </TITLE>
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+  The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+  security code used to reduce credit card fraud.<BR><BR>
+  <TABLE BORDER=0 CELLSPACING=4>
+    <TR>
+      <TH>Visa / MasterCard / Discover</TH>
+      <TH>American Express</TH>
+    </TR>
+    <TR>
+      <TD>
+        <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="cvv2.png">
+      </TD>
+      <TD>
+        <IMG BORDER=0 ALT="American Express" SRC="cvv2_amex.png">
+      </TD>
+  </TABLE>
+    <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+  </BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.png b/fs_selfservice/FS-SelfService/cgi/cvv2.png
new file mode 100644 (file)
index 0000000..4610dcb
Binary files /dev/null and b/fs_selfservice/FS-SelfService/cgi/cvv2.png differ
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png
new file mode 100644 (file)
index 0000000..21c36a0
Binary files /dev/null and b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png differ
diff --git a/fs_selfservice/FS-SelfService/cgi/list_customers.html b/fs_selfservice/FS-SelfService/cgi/list_customers.html
new file mode 100644 (file)
index 0000000..6d4ba56
--- /dev/null
@@ -0,0 +1,41 @@
+<HTML><HEAD><TITLE>Reseller Main</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller Main</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR><TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+<A HREF="<%= $url %>agent_main">Reseller Main</A><BR>
+<!-- <A HREF="<%= $url %>other">SomethingElse</A><BR> -->
+</TD><TD VALIGN="top">
+
+<%=
+  if ( @customers ) {
+    $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+            '<TR><TH BGCOLOR="#cccccc" COLSPAN=3>Customers</TH><TD>';
+    my $col1 = "ffffff";
+    my $col2 = "dddddd";
+    my $col = $col1;
+
+    foreach my $customer ( @customers ) {
+      my $td = qq!<TD BGCOLOR="#$col">!;
+      my $a = qq!<A HREF="${url}view_customer;custnum=!. 
+              $customer->{'custnum'}. '">';
+      $OUT .=
+        '<TR>'.
+        "$td<FONT COLOR=\"". $customer->{'statuscolor'}. '">'.
+          ucfirst($customer->{'status'}). "</TD>". "$td</TD>".
+        "$td$a". $customer->{'name'}. "</A></TD>".
+        '</TR>';
+        #"$td</TD>".
+      $col = $col eq $col1 ? $col2 : $col1;
+    }
+    $OUT .= '</TABLE>';
+  } else {
+    $OUT .= 'No customers.<BR><BR>';
+  }
+%>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
diff --git a/fs_selfservice/FS-SelfService/cgi/signup.html b/fs_selfservice/FS-SelfService/cgi/signup.html
new file mode 100755 (executable)
index 0000000..9730351
--- /dev/null
@@ -0,0 +1,233 @@
+<HTML><HEAD><TITLE><%= $agent || 'ISP' %> Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+  var mywindow = -1;
+  function myopen(filename,windowname,properties) {
+    myclose();
+    mywindow = window.open(filename,windowname,properties);
+  }
+  function myclose() {
+    if ( mywindow != -1 )
+      mywindow.close();
+    mywindow = -1
+  }
+//--></script>
+<FONT SIZE=7><%= $agent || 'ISP' %> Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_signup">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+  $OUT .= '<OPTION VALUE="">' unless $refnum;
+  foreach my $part_referral ( @part_referral ) {
+    $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+    $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+    $OUT .= '>'. $part_referral->{'referral'};
+  }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+  <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+                <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Company</TD>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+  <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">&nbsp;</TD>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+  <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+  <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+  <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+  <TD>
+    <%=
+        ($county_html, $state_html, $country_html) =
+          regionselector( {
+            selected_county  => $county,
+            selected_state   => $state,
+            selected_country => $country,
+            default_state    => $statedefault,
+            default_country  => $countrydefault,
+            locales          => \@cust_main_county,
+          } );
+        "$county_html $state_html";
+    %>
+  </TD>
+  <TH><font color="#ff0000">*</font>Zip</TH>
+  <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+  <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+  <TD><%= $country_html %></TD>
+<TR>
+  <TD ALIGN="right">Day Phone</TD>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Night Phone</TD>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Fax</TD>
+  <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+  <%=
+    $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+    my @invoicing_list = split(', ', $invoicing_list );
+    $OUT .= ' CHECKED'
+      if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+    $OUT .= '>';
+  %>
+
+  Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+  <%=
+
+    my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+    my %types = (
+                  'VISA' => 'VISA card',
+                  'MasterCard' => 'MasterCard',
+                  'Discover' => 'Discover card',
+                  'American Express' => 'American Express card',
+                );
+    foreach ( keys %types ) {
+      $selected = $CARD_type eq $types{$_} ? 'SELECTED' : '';
+      $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+    }
+    $cardselect .= '</SELECT>';
+  
+    my %payby = (
+      'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+      'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+      'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+      'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+      'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+      'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+      'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+      'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+    );
+
+    if ( $cvv_enabled ) {
+      foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+        $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+      }
+    }
+
+    my( $account, $aba ) = split('@', $payinfo);
+    my %paybychecked = (
+      'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+      'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+      'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+      'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+      'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+      'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+      'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+      'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+    );
+
+    if ( $cvv_enabled ) {
+      foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+        $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+      }
+    }
+
+    for (@payby) {
+      if ( scalar(@payby) == 1) {
+        $OUT .= '<TD VALIGN=TOP>'.
+                qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+                "$paybychecked{$_}</TD>";
+      } else {
+        $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+        if ($payby eq $_) {
+          $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+        } else {
+          $OUT .= qq!> $payby{$_}</TD>!;
+        }
+
+      }
+    }
+  %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+  <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+  <%=
+    foreach my $part_pkg ( @part_pkg ) {
+      $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+      $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+      $OUT .= '>'. $part_pkg->{'pkg'};
+    }
+  %>
+
+  </SELECT></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Username</TD>
+  <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Password</TD>
+  <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Re-enter Password</TD>
+  <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+  if ( $security_phrase ) {
+    $OUT .= <<ENDOUT;
+<TR>
+  <TD ALIGN="right">Security Phrase</TD>
+  <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+  </TD>
+</TR>
+ENDOUT
+  } else {
+    $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+  }
+%>
+<%=
+  if ( @svc_acct_pop ) {
+    $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+            popselector( 'popnum'        => $popnum,
+                         'pops'          => \@svc_acct_pop,
+                         'init_popstate' => $init_popstate,
+                         'popac'         => $popac,
+                         'acstate'       => $acstate,
+                       ).
+            '</TD></TR>';
+  } else {
+    $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+  }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_customer.html b/fs_selfservice/FS-SelfService/cgi/view_customer.html
new file mode 100644 (file)
index 0000000..e4e9be2
--- /dev/null
@@ -0,0 +1,84 @@
+<HTML><HEAD><TITLE>View Customer</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>View Customer</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR><TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+<A HREF="<%= $url %>agent_main">Reseller Main</A><BR>
+<!-- <A HREF="<%= $url %>other">SomethingElse</A><BR> -->
+</TD><TD VALIGN="top">
+
+<%= $message
+      ? "<FONT SIZE=\"+2\"><B>$message</B></FONT><BR><BR>"
+      : ''
+%>
+
+<%= $small_custview %>
+
+<BR>Purchase additional package
+<FORM ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_order_pkg">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+<TR>
+  <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">
+
+  <%=
+    foreach my $part_pkg ( @part_pkg ) {
+      $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+      $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+      $OUT .= '>'. $part_pkg->{'pkg'};
+    }
+  %>
+
+  </SELECT></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Username</TD>
+  <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Password</TD>
+  <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+  <TD ALIGN="right">Re-enter Password</TD>
+  <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+  if ( $security_phrase ) {
+    $OUT .= <<ENDOUT;
+<TR>
+  <TD ALIGN="right">Security Phrase</TD>
+  <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+  </TD>
+</TR>
+ENDOUT
+  } else {
+    $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+  }
+%>
+<%=
+  if ( @svc_acct_pop ) {
+    $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+            popselector( 'popnum'        => $popnum,
+                         'pops'          => \@svc_acct_pop,
+                         'init_popstate' => $init_popstate,
+                         'popac'         => $popac,
+                         'acstate'       => $acstate,
+                       ).
+            '</TD></TR>';
+  } else {
+    $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+  }
+%>
+</TABLE>
+<INPUT TYPE="submit" VALUE="Purchase">
+</FORM>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
index fb2b12f..b646bac 100644 (file)
@@ -1,7 +1,7 @@
 package FS::SignupClient;
 
 use strict;
-use vars qw($VERSION @ISA @EXPORT_OK); # $fs_signupd_socket);
+use vars qw($VERSION @ISA @EXPORT_OK $init_data); # $fs_signupd_socket);
 use Exporter;
 #use Socket;
 #use FileHandle;
@@ -12,7 +12,7 @@ use FS::SelfService; # qw( new_customer signup_info );
 $VERSION = '0.04';
 
 @ISA = qw( Exporter );
-@EXPORT_OK = qw( signup_info new_customer );
+@EXPORT_OK = qw( signup_info new_customer regionselector );
 
 =head1 NAME
 
@@ -99,7 +99,7 @@ Each hash reference has the following keys:
 #compatibility bit
 sub signup_info {
 
-  my $init_data = FS::SelfService::signup_info();
+  $init_data = FS::SelfService::signup_info();
 
   (map { $init_data->{$_} } qw( cust_main_county part_pkg svc_acct_pop ) ),
   $init_data;
@@ -148,10 +148,56 @@ sub new_customer {
   $hash->{'error'};
 }
 
+=item regionselector SELECTED_COUNTY, SELECTED_STATE, SELECTED_COUNTRY, PREFIX, ONCHANGE
+
+=cut
+
+sub regionselector {
+  my ( $selected_county, $selected_state, $selected_country,
+       $prefix, $onchange ) = @_;
+  signup_info() unless $init_data;
+  FS::SelfService::regionselector({
+    selected_county  => $selected_county,
+    selected_state   => $selected_state,
+    selected_country => $selected_country,
+    prefix           => $prefix,
+    onchange         => $onchange,
+    default_country  => $init_data->{countrydefault},
+    locales          => $init_data->{cust_main_county},
+  });
+    #default_state    => $init_data->{statedefault},
+}
+
+=item expselect PREFIX, DATE
+
+=cut
+
+sub expselect {
+  FS::SelfService::expselect(@_);
+}
+
+=item popselector 
+
+=cut
+
+sub popselector {
+  my( $popnum ) = @_;
+  signup_info() unless $init_data;
+  FS::SelfService::popselector({
+    popnum => $popnum,
+    pops   => $init_data->{svc_acct_pop},
+  });
+    #popac =>
+    #acstate =>
+}
+
 =back
 
 =head1 BUGS
 
+This is just a wrapper around FS::SelfService functions for backwards
+compatibility and will probably be deprecated soon.
+
 =head1 SEE ALSO
 
 L<fs_signupd>, L<FS::cust_main>
index 0a9a510..00a7aa6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -Tw
 #
-# $Id: signup.cgi,v 1.50 2004-01-04 03:52:54 ivan Exp $
+# $Id: signup.cgi,v 1.51 2004-06-10 12:31:32 ivan Exp $
 
 use strict;
 use vars qw( @payby $cgi $locales $packages
@@ -20,14 +20,14 @@ use vars qw( @payby $cgi $locales $packages
              $self_url
            );
 use subs qw( print_form print_okay print_decline
-             success_default decline_default
-             expselect );
+             success_default decline_default );
 use CGI;
 #use CGI::Carp qw(fatalsToBrowser);
 use Text::Template;
 use Business::CreditCard;
 use HTTP::BrowserDetect;
-use FS::SignupClient 0.03 qw( signup_info new_customer );
+use FS::SignupClient 0.03 qw( signup_info new_customer
+                              regionselector expselect popselector);
 
 #acceptable payment methods
 #
@@ -360,265 +360,6 @@ sub print_okay {
   }
 }
 
-#horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
-sub popselector {
-
-  my( $popnum ) = @_;
-
-  return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
-  return $pops->[0]{city}. ', '. $pops->[0]{state}.
-         ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
-         '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
-    if scalar(@$pops) == 1;
-
-  #my %pop = ();
-  #my %popnum2pop = ();
-  #foreach (@$pops) {
-  #  push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
-  #  $popnum2pop{$_->{popnum}} = $_;
-  #}
-
-  my $text = <<END;
-    <SCRIPT>
-    function opt(what,href,text) {
-      var optionName = new Option(text, href, false, false)
-      var length = what.length;
-      what.options[length] = optionName;
-    }
-END
-
-  if ( $init_popstate ) {
-    $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
-             $init_popstate. '">';
-  } else {
-    $text .= <<END;
-      function acstate_changed(what) {
-        state = what.options[what.selectedIndex].text;
-        what.form.popac.options.length = 0
-        what.form.popac.options[0] = new Option("Area code", "-1", false, true);
-END
-  } 
-
-  my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
-  foreach my $state ( sort { $a cmp $b } @states ) {
-    $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
-
-    foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
-      $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
-      if ($ac eq $cgi->param('popac')) {
-        $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
-      }
-    }
-    $text .= "}\n" unless $init_popstate;
-  }
-  $text .= "popac_changed(what.form.popac)}\n";
-
-  $text .= <<END;
-  function popac_changed(what) {
-    ac = what.options[what.selectedIndex].text;
-    what.form.popnum.options.length = 0;
-    what.form.popnum.options[0] = new Option("City", "-1", false, true);
-
-END
-
-  foreach my $state ( @states ) {
-    foreach my $popac ( keys %{ $pop{$state} } ) {
-      $text .= "\nif ( ac == \"$popac\" ) {\n";
-
-      foreach my $pop ( @{$pop{$state}->{$popac}}) {
-        my $o_popnum = $pop->{popnum};
-        my $poptext =  $pop->{city}. ', '. $pop->{state}.
-                       ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
-
-        $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
-        if ($popnum == $o_popnum) {
-          $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
-        }
-      }
-      $text .= "}\n";
-    }
-  }
-
-
-  $text .= "}\n</SCRIPT>\n";
-
-  $text .=
-    qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
-    qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
-  $text .= "<OPTION" . ($_ eq $cgi->param('acstate') ? " SELECTED" : "") .
-           ">$_" foreach sort { $a cmp $b } @states;
-  $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
-
-  $text .=
-    qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
-    qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
-
-  $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
-
-
-  #comment this block to disable initial list polulation
-  my @initial_select = ();
-  if ( scalar( @$pops ) > 100 ) {
-    push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
-  } else {
-    @initial_select = @$pops;
-  }
-  foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
-    $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
-             ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
-             $pop->{city}. ', '. $pop->{state}.
-               ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
-  }
-
-  $text .= qq!</SELECT></TD></TR></TABLE>!;
-
-  $text;
-
-}
-
-sub expselect {
-  my $prefix = shift;
-  my $date = shift || '';
-  my( $m, $y ) = ( 0, 0 );
-  if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
-    ( $m, $y ) = ( $2, $1 );
-  } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
-    ( $m, $y ) = ( $1, $3 );
-  }
-  my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
-  for ( 1 .. 12 ) {
-    $return .= "<OPTION";
-    $return .= " SELECTED" if $_ == $m;
-    $return .= ">$_";
-  }
-  $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
-  for ( 2001 .. 2037 ) {
-    $return .= "<OPTION";
-    $return .= " SELECTED" if $_ == $y;
-    $return .= ">$_";
-  }
-  $return .= "</SELECT>";
-
-  $return;
-}
-
-#false laziness w/FS::cust_main_county
-sub regionselector {
-  my ( $selected_county, $selected_state, $selected_country,
-       $prefix, $onchange ) = @_;
-
-  $prefix = '' unless defined $prefix;
-
-  my $countyflag = 0;
-
-  my %cust_main_county;
-
-#  unless ( @cust_main_county ) { #cache 
-    #@cust_main_county = qsearch('cust_main_county', {} );
-    #foreach my $c ( @cust_main_county ) {
-    foreach my $c ( @$locales ) {
-      #$countyflag=1 if $c->county;
-      $countyflag=1 if $c->{county};
-      #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
-      #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
-      $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
-    }
-#  }
-  $countyflag=1 if $selected_county;
-
-  my $script_html = <<END;
-    <SCRIPT>
-    function opt(what,value,text) {
-      var optionName = new Option(text, value, false, false);
-      var length = what.length;
-      what.options[length] = optionName;
-    }
-    function ${prefix}country_changed(what) {
-      country = what.options[what.selectedIndex].text;
-      for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
-          what.form.${prefix}state.options[i] = null;
-END
-      #what.form.${prefix}state.options[0] = new Option('', '', false, true);
-
-  foreach my $country ( sort keys %cust_main_county ) {
-    $script_html .= "\nif ( country == \"$country\" ) {\n";
-    foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
-      my $text = $state || '(n/a)';
-      $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
-    }
-    $script_html .= "}\n";
-  }
-
-  $script_html .= <<END;
-    }
-    function ${prefix}state_changed(what) {
-END
-
-  if ( $countyflag ) {
-    $script_html .= <<END;
-      state = what.options[what.selectedIndex].text;
-      country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
-      for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
-          what.form.${prefix}county.options[i] = null;
-END
-
-    foreach my $country ( sort keys %cust_main_county ) {
-      $script_html .= "\nif ( country == \"$country\" ) {\n";
-      foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
-        $script_html .= "\nif ( state == \"$state\" ) {\n";
-          #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
-          foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
-            my $text = $county || '(n/a)';
-            $script_html .=
-              qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
-          }
-        $script_html .= "}\n";
-      }
-      $script_html .= "}\n";
-    }
-  }
-
-  $script_html .= <<END;
-    }
-    </SCRIPT>
-END
-
-  my $county_html = $script_html;
-  if ( $countyflag ) {
-    $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$onchange">!;
-    $county_html .= '</SELECT>';
-  } else {
-    $county_html .=
-      qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$selected_county">!;
-  }
-
-  my $state_html = qq!<SELECT NAME="${prefix}state" !.
-                   qq!onChange="${prefix}state_changed(this); $onchange">!;
-  foreach my $state ( sort keys %{ $cust_main_county{$selected_country} } ) {
-    my $text = $state || '(n/a)';
-    my $selected = $state eq $selected_state ? 'SELECTED' : '';
-    $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
-  }
-  $state_html .= '</SELECT>';
-
-  $state_html .= '</SELECT>';
-
-  my $country_html = qq!<SELECT NAME="${prefix}country" !.
-                     qq!onChange="${prefix}country_changed(this); $onchange">!;
-  my $countrydefault = $init_data->{countrydefault} || 'US';
-  foreach my $country (
-    sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
-      keys %cust_main_county
-  ) {
-    my $selected = $country eq $selected_country ? ' SELECTED' : '';
-    $country_html .= "\n<OPTION$selected>$country</OPTION>"
-  }
-  $country_html .= '</SELECT>';
-
-  ($county_html, $state_html, $country_html);
-
-}
-
 sub success_default { #html to use if you don't specify a success file
   <<'END';
 <HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
index 2eef5bb..af32887 100755 (executable)
@@ -95,10 +95,30 @@ foreach my $agent ( sort {
           <%= $agent->agent %></A></TD>
         <TD><A HREF="<%=$p%>edit/agent_type.cgi?<%= $agent->typenum %>"><%= $agent->agent_type->atype %></A></TD>
         <TD>
-          <FONT COLOR="#00CC00"><B><%= $num_ncancelled %></B></FONT>
-            <A HREF="<%= $cust_main_link %>&showcancelledcustomers=0">active</A>
-          <BR><FONT COLOR="#FF0000"><B><%= $num_cancelled %></B></FONT>
-            <A HREF="<%= $cust_main_link %>&showcancelledcustomers=1&cancelled=1">cancelled</A>
+
+          <B>
+            <%= my $num_prospect = $agent->num_prospect_cust_main %>
+          </B>
+          <% if ( $num_prospect ) { %>
+            <A HREF="<%= $cust_main_link %>&prospect=1"><% } %>prospects<% if ($num_prospect ) { %></A><% } %>
+
+          <BR><FONT COLOR="#00CC00"><B>
+            <%= my $num_active = $agent->num_active_cust_main %>
+          </B></FONT>
+          <% if ( $num_active ) { %>
+            <A HREF="<%= $cust_main_link %>&active=1"><% } %>active<% if ( $num_active ) { %></A><% } %>
+
+          <BR><FONT COLOR="#FF9900"><B>
+            <%= my $num_susp = $agent->num_susp_cust_main %>
+          </B></FONT>
+          <% if ( $num_susp ) { %>
+            <A HREF="<%= $cust_main_link %>&suspended=1"><% } %>suspended<% if ( $num_susp ) { %></A><% } %>
+
+          <BR><FONT COLOR="#FF0000"><B>
+            <%= my $num_cancel = $agent->num_cancel_cust_main %>
+          </B></FONT>
+          <% if ( $num_cancel ) { %>
+            <A HREF="<%= $cust_main_link %>&showcancelledcustomers=1&cancelled=1"><% } %>cancelled<% if ( $num_cancel ) { %></A><% } %>
         </TD>
         <TD><%= $agent->freq %></TD>
         <TD><%= $agent->prog %></TD>
index 69a78d6..632d68d 100755 (executable)
@@ -82,119 +82,62 @@ if ( $cgi->param('browse')
     }
   }
 
-  my $ncancelled = '';
+  my @qual = ();
 
-  if ( driver_name eq 'mysql' ) {
-
-       my $sql = "CREATE TEMPORARY TABLE temp1_$$ TYPE=MYISAM
-                    SELECT cust_pkg.custnum,COUNT(*) as count
-                      FROM cust_pkg,cust_main
-                        WHERE cust_pkg.custnum = cust_main.custnum
-                              AND ( cust_pkg.cancel IS NULL
-                                    OR cust_pkg.cancel = 0 )
-                        GROUP BY cust_pkg.custnum";
-       my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
-       $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
-       $sql = "CREATE TEMPORARY TABLE temp2_$$ TYPE=MYISAM
-                 SELECT cust_pkg.custnum,COUNT(*) as count
-                   FROM cust_pkg,cust_main
-                     WHERE cust_pkg.custnum = cust_main.custnum
-                     GROUP BY cust_pkg.custnum";
-       $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
-       $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
-  }
+  my $ncancelled = '';
 
   if (  $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
        || ( $conf->exists('hidecancelledcustomers')
              && ! $cgi->param('showcancelledcustomers') )
      ) {
     #grep { $_->ncancelled_pkgs || ! $_->all_pkgs }
-    if ( driver_name eq 'mysql' ) {
-       $ncancelled = "
-          temp1_$$.custnum = cust_main.custnum
-               AND temp2_$$.custnum = cust_main.custnum
-               AND (temp1_$$.count > 0
-                       OR temp2_$$.count = 0 )
-       ";
-
-    } else {
-       $ncancelled = "
-          0 < ( SELECT COUNT(*) FROM cust_pkg
-                       WHERE cust_pkg.custnum = cust_main.custnum
-                         AND ( cust_pkg.cancel IS NULL
-                               OR cust_pkg.cancel = 0
-                             )
-                   )
-            OR 0 = ( SELECT COUNT(*) FROM cust_pkg
-                       WHERE cust_pkg.custnum = cust_main.custnum
-                   )
-       ";
-     }
-   }
-
-  my $cancelled = '';
-  if ( $cgi->param('cancelled') ) {
-    $cancelled = "
-      0 = ( SELECT COUNT(*) FROM cust_pkg
-                   WHERE cust_pkg.custnum = cust_main.custnum
-                      AND ( cust_pkg.cancel IS NULL
-                            OR cust_pkg.cancel = 0
-                          )
-          )
-        AND 0 < ( SELECT COUNT(*) FROM cust_pkg
+    push @qual, "
+       ( 0 < ( SELECT COUNT(*) FROM cust_pkg
+                      WHERE cust_pkg.custnum = cust_main.custnum
+                        AND ( cust_pkg.cancel IS NULL
+                              OR cust_pkg.cancel = 0
+                            )
+             )
+         OR 0 = ( SELECT COUNT(*) FROM cust_pkg
                     WHERE cust_pkg.custnum = cust_main.custnum
                 )
+       )
     ";
-  }
+   }
+
+  push @qual, FS::cust_main->cancel_sql   if $cgi->param('cancelled');
+  push @qual, FS::cust_main->prospect_sql if $cgi->param('prospect');
+  push @qual, FS::cust_main->active_sql   if $cgi->param('active');
+  push @qual, FS::cust_main->susp_sql     if $cgi->param('suspended');
 
   #EWWWWWW
   my $qual = join(' AND ',
             map { "$_ = ". dbh->quote($search{$_}) } keys %search );
 
-  if ( $cancelled ) {
-    $qual .= ' AND ' if $qual;
-    $qual .= $cancelled;
-  } elsif ( $ncancelled ) {
+  my $addl_qual = join(' AND ', @qual);
+
+  if ( $addl_qual ) {
     $qual .= ' AND ' if $qual;
-    $qual .= $ncancelled;
+    $qual .= $addl_qual;
   }
     
   $qual = " WHERE $qual" if $qual;
-  my $statement;
-  if ( driver_name eq 'mysql' ) {
-    $statement = "SELECT COUNT(*) FROM cust_main";
-    $statement .= ", temp1_$$, temp2_$$ $qual" if $qual;
-  } else {
-    $statement = "SELECT COUNT(*) FROM cust_main $qual";
-  }
+  my $statement = "SELECT COUNT(*) FROM cust_main $qual";
   my $sth = dbh->prepare($statement) or die dbh->errstr." preparing $statement";
   $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
 
   $total = $sth->fetchrow_arrayref->[0];
 
-  my $rqual = $cancelled || $ncancelled;
-  if ( $rqual ) {
+  if ( $addl_qual ) {
     if ( %search ) {
-      $rqual = " AND $rqual";
+      $addl_qual = " AND $addl_qual";
     } else {
-      $rqual = " WHERE $rqual";
+      $addl_qual = " WHERE $addl_qual";
     }
   }
 
-  my @just_cust_main;
-  if ( driver_name eq 'mysql' ) {
-    @just_cust_main = qsearch('cust_main', \%search, 'cust_main.*',
-                              ",temp1_$$,temp2_$$ $rqual $orderby $limit");
-  } else {
-    @just_cust_main = qsearch('cust_main', \%search, '',   
-                              "$rqual $orderby $limit" );
-  }
-  if ( driver_name eq 'mysql' ) {
-    my $sql = "DROP TABLE temp1_$$,temp2_$$;";
-    my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
-    $sth->execute or die "Error executing \"$sql\": ". $sth->errstr;
-  }
-  @cust_main = @just_cust_main;
+  @cust_main = qsearch('cust_main', \%search, '',   
+                         "$addl_qual $orderby $limit" );
 
 #  foreach my $cust_main ( @just_cust_main ) {
 #