agent virtualization, take one (stuff from "inactive" changeset snuck into cust_main...
authorivan <ivan>
Mon, 19 Jun 2006 02:33:52 +0000 (02:33 +0000)
committerivan <ivan>
Mon, 19 Jun 2006 02:33:52 +0000 (02:33 +0000)
17 files changed:
FS/FS/CurrentUser.pm [new file with mode: 0644]
FS/FS/Record.pm
FS/FS/Schema.pm
FS/FS/UID.pm
FS/FS/access_user.pm
FS/FS/cust_main.pm
FS/FS/part_pkg.pm
FS/MANIFEST
httemplate/elements/select-agent.html
httemplate/elements/select-table.html
httemplate/elements/tr-select-agent.html
httemplate/search/cust_bill.html
httemplate/search/cust_main.cgi
httemplate/search/cust_pkg.cgi
httemplate/search/svc_acct.cgi
httemplate/search/svc_domain.cgi
httemplate/search/svc_forward.cgi

diff --git a/FS/FS/CurrentUser.pm b/FS/FS/CurrentUser.pm
new file mode 100644 (file)
index 0000000..13d3416
--- /dev/null
@@ -0,0 +1,50 @@
+package FS::CurrentUser;
+
+use vars qw($CurrentUser);
+
+#not at compile-time, circular dependancey causes trouble
+#use FS::Record qw(qsearchs);
+#use FS::access_user;
+
+=head1 NAME
+
+FS::CurrentUser - Package representing the current user
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=cut
+
+sub load_user {
+  my( $class, $user ) = @_; #, $pass
+
+  #XXX remove me at some point
+  return "" if $user =~ /^fs_(queue|selfservice)$/;
+
+  #not the best thing in the world...
+  eval "use FS::Record qw(qsearchs);";
+  die $@ if $@;
+  eval "use FS::access_user;";
+  die $@ if $@;
+
+  $CurrentUser = qsearchs('access_user', {
+    'username' => $user,
+    #'_password' =>
+  } );
+
+  die "unknown user: $user" unless $CurrentUser; # or bad password
+
+  $CurrentUser;
+}
+
+=head1 BUGS
+
+Creepy crawlies
+
+=head1 SEE ALSO
+
+=cut
+
+1;
+
index 67de071..fa0d2d8 100644 (file)
@@ -29,14 +29,12 @@ $me = '[FS::Record]';
 
 $nowarn_identical = 0;
 
-my $conf;
 my $rsa_module;
 my $rsa_loaded;
 my $rsa_encrypt;
 my $rsa_decrypt;
 
 FS::UID->install_callback( sub {
-  $conf = new FS::Conf; 
   $File::CounterFile::DEFAULT_DIR = "/usr/local/etc/freeside/counters.". datasrc;
 } );
 
@@ -441,6 +439,7 @@ sub qsearch {
     }
 
     # Check for encrypted fields and decrypt them.
+    my $conf = new FS::Conf; 
     if ($conf->exists('encryption') && eval 'defined(@FS::'. $table . '::encrypted_fields)') {
       foreach my $record (@return) {
         foreach my $field (eval '@FS::'. $table . '::encrypted_fields') {
@@ -711,6 +710,7 @@ sub insert {
 
   
   # Encrypt before the database
+  my $conf = new FS::Conf;
   if ($conf->exists('encryption') && defined(eval '@FS::'. $table . 'encrypted_fields')) {
     foreach my $field (eval '@FS::'. $table . '::encrypted_fields') {
       $self->{'saved'} = $self->getfield($field);
@@ -727,12 +727,18 @@ sub insert {
   my @values = map { _quote( $self->getfield($_), $table, $_) } @real_fields;
   #eslaf
 
-  my $statement = "INSERT INTO $table ( ".
-      join( ', ', @real_fields ).
-    ") VALUES (".
-      join( ', ', @values ).
-    ")"
-  ;
+  my $statement = "INSERT INTO $table ";
+  if ( @real_fields ) {
+    $statement .=
+      "( ".
+        join( ', ', @real_fields ).
+      ") VALUES (".
+        join( ', ', @values ).
+       ")"
+    ;
+  } else {
+    $statement .= 'DEFAULT VALUES';
+  }
   warn "[debug]$me $statement\n" if $DEBUG > 1;
   my $sth = dbh->prepare($statement) or return dbh->errstr;
 
@@ -995,6 +1001,7 @@ sub replace {
   return $error if $error;
   
   # Encrypt for replace
+  my $conf = new FS::Conf;
   my $saved = {};
   if ($conf->exists('encryption') && defined(eval '@FS::'. $new->table . 'encrypted_fields')) {
     foreach my $field (eval '@FS::'. $new->table . '::encrypted_fields') {
@@ -1635,7 +1642,8 @@ sub virtual_fields {
                 "WHERE dbtable = '$table'";
     my $dbh = dbh;
     my $result = $dbh->selectcol_arrayref($query);
-    confess $dbh->errstr if $dbh->err;
+    confess "Error executing virtual fields query: $query: ". $dbh->errstr
+      if $dbh->err;
     $virtual_fields_cache{$table} = $result;
   }
 
@@ -1788,6 +1796,7 @@ sub encrypt {
   my ($self, $value) = @_;
   my $encrypted;
 
+  my $conf = new FS::Conf;
   if ($conf->exists('encryption')) {
     if ($self->is_encrypted($value)) {
       # Return the original value if it isn't plaintext.
@@ -1821,6 +1830,7 @@ sub is_encrypted {
 sub decrypt {
   my ($self,$value) = @_;
   my $decrypted = $value; # Will return the original value if it isn't encrypted or can't be decrypted.
+  my $conf = new FS::Conf;
   if ($conf->exists('encryption') && $self->is_encrypted($value)) {
     $self->loadRSA;
     if (ref($rsa_decrypt) =~ /::RSA/) {
@@ -1836,6 +1846,7 @@ sub loadRSA {
     #Initialize the Module
     $rsa_module = 'Crypt::OpenSSL::RSA'; # The Default
 
+    my $conf = new FS::Conf;
     if ($conf->exists('encryptionmodule') && $conf->config('encryptionmodule') ne '') {
       $rsa_module = $conf->config('encryptionmodule');
     }
index 17d541e..7219274 100644 (file)
@@ -16,12 +16,13 @@ use FS::UID qw(datasrc);
 $DEBUG = 0;
 $me = '[FS::Schema]';
 
-#ask FS::UID to run this stuff for us later
-FS::UID->install_callback( sub {
-  #$conf = new FS::Conf; 
-  &reload_dbdef("/usr/local/etc/freeside/dbdef.". datasrc)
-    unless $setup_hack; #$setup_hack needed now?
-} );
+#hardcoded now...
+##ask FS::UID to run this stuff for us later
+#FS::UID->install_callback( sub {
+#  #$conf = new FS::Conf; 
+#  &reload_dbdef("/usr/local/etc/freeside/dbdef.". datasrc)
+#    unless $setup_hack; #$setup_hack needed now?
+#} );
 
 =head1 NAME
 
index 83b652b..21df944 100644 (file)
@@ -13,6 +13,7 @@ use Exporter;
 use Carp qw(carp croak cluck);
 use DBI;
 use FS::Conf;
+use FS::CurrentUser;
 
 @ISA = qw(Exporter);
 @EXPORT_OK = qw(checkeuid checkruid cgisuidsetup adminsuidsetup forksuidsetup
@@ -87,6 +88,12 @@ sub forksuidsetup {
 
   $dbh = &myconnect;
 
+  use FS::Schema qw(reload_dbdef);
+  reload_dbdef("/usr/local/etc/freeside/dbdef.$datasrc")
+    unless $FS::Schema::setup_hack;
+
+  FS::CurrentUser->load_user($user);
+
   foreach ( keys %callback ) {
     &{$callback{$_}};
     # breaks multi-database installs # delete $callback{$_}; #run once
@@ -98,7 +105,11 @@ sub forksuidsetup {
 }
 
 sub myconnect {
-  DBI->connect( getsecrets, {'AutoCommit' => 0, 'ChopBlanks' => 1, } )
+  DBI->connect( getsecrets, { 'AutoCommit'         => 0,
+                              'ChopBlanks'         => 1,
+                              'ShowErrorStatement' => 1,
+                            }
+              )
     or die "DBI->connect error: $DBI::errstr\n";
 }
 
@@ -256,10 +267,10 @@ sub getsecrets {
   $user = $setuser if $setuser;
   die "No user!" unless $user;
   my($conf) = new FS::Conf $conf_dir;
-  my($line) = grep /^\s*$user\s/, $conf->config('mapsecrets');
+  my($line) = grep /^\s*($user|\*)\s/, $conf->config('mapsecrets');
   die "User $user not found in mapsecrets!" unless $line;
-  $line =~ /^\s*$user\s+(.*)$/;
-  $secrets = $1;
+  $line =~ /^\s*($user|\*)\s+(.*)$/;
+  $secrets = $2;
   die "Illegal mapsecrets line for user?!" unless $secrets;
   ($datasrc, $db_user, $db_pass) = $conf->config($secrets)
     or die "Can't get secrets: $secrets: $!\n";
index ca311d3..c95d029 100644 (file)
@@ -2,7 +2,7 @@ package FS::access_user;
 
 use strict;
 use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( qsearch qsearchs dbh );
 use FS::m2m_Common;
 use FS::access_usergroup;
 
@@ -29,7 +29,7 @@ FS::access_user - Object methods for access_user records
 
 =head1 DESCRIPTION
 
-An FS::access_user object represents an example.  FS::access_user inherits from
+An FS::access_user object represents an internal access user.  FS::access_user inherits from
 FS::Record.  The following fields are currently supported:
 
 =over 4
@@ -52,7 +52,7 @@ FS::Record.  The following fields are currently supported:
 
 =item new HASHREF
 
-Creates a new example.  To add the example to the database, see L<"insert">.
+Creates a new internal access user.  To add the user to the database, see L<"insert">.
 
 Note that this stores the hash reference, not a distinct copy of the hash it
 points to.  You can ask the object for a copy with the I<hash> method.
@@ -91,7 +91,7 @@ returns the error, otherwise returns false.
 
 =item check
 
-Checks all fields to make sure this is a valid example.  If there is
+Checks all fields to make sure this is a valid internal access user.  If there is
 an error, returns the error, otherwise returns false.  Called by the insert
 and replace methods.
 
@@ -151,12 +151,51 @@ sub access_usergroup {
 #
 #}
 
+=item agentnums 
+
+Returns a list of agentnums this user can view (via group membership).
+
+=cut
+
+sub agentnums {
+  my $self = shift;
+  my $sth = dbh->prepare(
+    "SELECT DISTINCT agentnum FROM access_usergroup
+                              JOIN access_groupagent USING ( groupnum )
+       WHERE usernum = ?"
+  ) or die dbh->errstr;
+  $sth->execute($self->usernum) or die $sth->errstr;
+  map { $_->[0] } @{ $sth->fetchall_arrayref };
+}
+
+=item agentnums_href
+
+Returns a hashref of agentnums this user can view.
+
+=cut
+
+sub agentnums_href {
+  my $self = shift;
+  { map { $_ => 1 } $self->agentnums };
+}
+
+=item agentnums_sql
+
+Returns an sql fragement to select only agentnums this user can view.
+
+=cut
+
+sub agentnums_sql {
+  my $self = shift;
+  '( '.
+    join( ' OR ', map "agentnum = $_", $self->agentnums ).
+  ' )';
+}
+
 =back
 
 =head1 BUGS
 
-The author forgot to customize this manpage.
-
 =head1 SEE ALSO
 
 L<FS::Record>, schema.html from the base documentation.
index 2763c13..8956d5b 100644 (file)
@@ -20,6 +20,7 @@ use Date::Parse;
 #use Date::Manip;
 use String::Approx qw(amatch);
 use Business::CreditCard 0.28;
+use Locale::Country;
 use FS::UID qw( getotaker dbh );
 use FS::Record qw( qsearchs qsearch dbdef );
 use FS::Misc qw( send_email );
@@ -79,7 +80,7 @@ sub _cache {
   my $self = shift;
   my ( $hashref, $cache ) = @_;
   if ( exists $hashref->{'pkgnum'} ) {
-#    #@{ $self->{'_pkgnum'} } = ();
+    #@{ $self->{'_pkgnum'} } = ();
     my $subcache = $cache->subcache( 'pkgnum', 'cust_pkg', $hashref->{custnum});
     $self->{'_pkgnum'} = $subcache;
     #push @{ $self->{'_pkgnum'} },
@@ -3186,6 +3187,7 @@ This interface may change in the future.
 
 sub invoicing_list {
   my( $self, $arrayref ) = @_;
+
   if ( $arrayref ) {
     my @cust_main_invoice;
     if ( $self->custnum ) {
@@ -3220,12 +3222,14 @@ sub invoicing_list {
       warn $error if $error;
     }
   }
+  
   if ( $self->custnum ) {
     map { $_->address }
       qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
   } else {
     ();
   }
+
 }
 
 =item check_invoicing_list ARRAYREF
@@ -3303,6 +3307,18 @@ sub invoicing_list_addpost {
   $self->invoicing_list(\@invoicing_list);
 }
 
+=item invoicing_list_emailonly
+
+Returns the list of email invoice recipients (invoicing_list without non-email
+destinations such as POST and FAX).
+
+=cut
+
+sub invoicing_list_emailonly {
+  my $self = shift;
+  grep { $_ !~ /^([A-Z]+)$/ } $self->invoicing_list;
+}
+
 =item referral_cust_main [ DEPTH [ EXCLUDE_HASHREF ] ]
 
 Returns an array of customers referred by this customer (referral_custnum set
@@ -3600,6 +3616,17 @@ sub ship_contact {
     : $self->contact;
 }
 
+=item country_full
+
+Returns this customer's full country name
+
+=cut
+
+sub country_full {
+  my $self = shift;
+  code2country($self->country);
+}
+
 =item status
 
 Returns a status string for this customer, currently:
@@ -3610,6 +3637,8 @@ Returns a status string for this customer, currently:
 
 =item active - One or more recurring packages is active
 
+=item inactive - No active recurring packages, but otherwise unsuspended/uncancelled (the inactive status is new - previously inactive customers were mis-identified as cancelled)
+
 =item suspended - All non-cancelled recurring packages are suspended
 
 =item cancelled - All recurring packages are cancelled
@@ -3620,7 +3649,7 @@ Returns a status string for this customer, currently:
 
 sub status {
   my $self = shift;
-  for my $status (qw( prospect active suspended cancelled )) {
+  for my $status (qw( prospect active inactive 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;
@@ -3635,14 +3664,18 @@ 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;
+
+  my %statuscolor = (
+    'prospect'  => '7e0079', #'000000', #black?  naw, purple
+    'active'    => '00CC00', #green
+    'inactive'  => '0000CC', #blue
+    'suspended' => 'FF9900', #yellow
+    'cancelled' => 'FF0000', #red
+  );
+
   $statuscolor{$self->status};
 }
 
@@ -3659,25 +3692,40 @@ with no packages ever ordered)
 
 =cut
 
+use vars qw($select_count_pkgs);
+$select_count_pkgs =
+  "SELECT COUNT(*) FROM cust_pkg
+    WHERE cust_pkg.custnum = cust_main.custnum";
+
 sub prospect_sql { "
-  0 = ( SELECT COUNT(*) FROM cust_pkg
-          WHERE cust_pkg.custnum = cust_main.custnum
-      )
+  0 = ( $select_count_pkgs )
 "; }
 
 =item active_sql
 
-Returns an SQL expression identifying active cust_main records.
+Returns an SQL expression identifying active cust_main records (customers with
+no active recurring packages, but otherwise unsuspended/uncancelled).
 
 =cut
 
 sub active_sql { "
-  0 < ( SELECT COUNT(*) FROM cust_pkg
-          WHERE cust_pkg.custnum = cust_main.custnum
-            AND ". FS::cust_pkg->active_sql. "
+  0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. "
       )
 "; }
 
+=item inactive_sql
+
+Returns an SQL expression identifying inactive cust_main records (customers with
+active recurring packages).
+
+=cut
+
+sub inactive_sql { "
+  0 = ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " )
+  AND
+  0 < ( $select_count_pkgs AND ". FS::cust_pkg->inactive_sql. " )
+"; }
+
 =item susp_sql
 =item suspended_sql
 
@@ -3685,23 +3733,12 @@ Returns an SQL expression identifying suspended cust_main records.
 
 =cut
 
-#my $recurring_sql = FS::cust_pkg->recurring_sql;
-my $recurring_sql = "
-  '0' != ( select freq from part_pkg
-             where cust_pkg.pkgpart = part_pkg.pkgpart )
-";
 
 sub suspended_sql { susp_sql(@_); }
 sub susp_sql { "
-    0 < ( SELECT COUNT(*) FROM cust_pkg
-            WHERE cust_pkg.custnum = cust_main.custnum
-              AND $recurring_sql
-              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 ". FS::cust_pkg->active_sql. "
-            )
+    0 < ( $select_count_pkgs AND ". FS::cust_pkg->suspended_sql. " )
+    AND
+    0 = ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " )
 "; }
 
 =item cancel_sql
@@ -3712,16 +3749,21 @@ 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 $recurring_sql
-                AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
-          )
-"; }
+sub cancel_sql {
+
+  my $recurring_sql = FS::cust_pkg->recurring_sql;
+  #my $recurring_sql = "
+  #  '0' != ( select freq from part_pkg
+  #             where cust_pkg.pkgpart = part_pkg.pkgpart )
+  #";
+
+  "
+    0 < ( $select_count_pkgs )
+    AND 0 = ( $select_count_pkgs AND $recurring_sql
+                  AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+            )
+  ";
+}
 
 =item uncancel_sql
 =item uncancelled_sql
@@ -3732,15 +3774,12 @@ Returns an SQL expression identifying un-cancelled cust_main records.
 
 sub uncancelled_sql { uncancel_sql(@_); }
 sub uncancel_sql { "
-  ( 0 < ( SELECT COUNT(*) FROM cust_pkg
-                 WHERE cust_pkg.custnum = cust_main.custnum
+  ( 0 < ( $select_count_pkgs
                    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
-           )
+    OR 0 = ( $select_count_pkgs )
   )
 "; }
 
@@ -3802,11 +3841,18 @@ Returns a (possibly empty) array of FS::cust_main objects.
 sub smart_search {
   my %options = @_;
   my $search = delete $options{'search'};
-  my @cust_main = ();
 
+  #here is the agent virtualization
+  my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+  my @cust_main = ();
   if ( $search =~ /^\s*(\d+)\s*$/ ) { # customer # search
 
-    push @cust_main, qsearch('cust_main', { 'custnum' => $1, %options } );
+    push @cust_main, qsearch( {
+      'table'     => 'cust_main',
+      'hashref'   => { 'custnum' => $1, %options },
+      'extra_sql' => " AND $agentnums_sql", #agent virtualization
+    } );
 
   } elsif ( $search =~ /^\s*(\S.*\S)\s*$/ ) { #value search
 
@@ -3820,50 +3866,65 @@ sub smart_search {
       if defined dbdef->table('cust_main')->column('ship_last');
     $sql .= ' )';
 
-    push @cust_main, qsearch( 'cust_main', \%options, '', $sql );
+    push @cust_main, qsearch( {
+      'table'     => 'cust_main',
+      'hashref'   => \%options,
+      'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+    } );
 
     unless ( @cust_main ) {  #no exact match, trying substring/fuzzy
 
       #still some false laziness w/ search/cust_main.cgi
 
       #substring
-      push @cust_main, qsearch( 'cust_main',
-                                { 'last'     => { 'op'    => 'ILIKE',
-                                                  'value' => "%$q_value%" },
-                                  %options,
-                                }
-                              );
-      push @cust_main, qsearch( 'cust_main',
-                                { 'ship_last' => { 'op'    => 'ILIKE',
-                                                   'value' => "%$q_value%" },
-                                  %options,
-
-                                }
-                              )
+      push @cust_main, qsearch( {
+        'table'     => 'cust_main',
+        'hashref'   => { 'last'     => { 'op'    => 'ILIKE',
+                                         'value' => "%$q_value%" },
+                         %options,
+                       },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualizaiton
+      } );
+      push @cust_main, qsearch( {
+        'table'     => 'cust_main',
+        'hashref'   => { 'ship_last' => { 'op'     => 'ILIKE',
+                                          'value' => "%$q_value%" },
+                         %options, 
+                       },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualization
+      } )
         if defined dbdef->table('cust_main')->column('ship_last');
 
-      push @cust_main, qsearch( 'cust_main',
-                                { 'company'  => { 'op'    => 'ILIKE',
-                                                  'value' => "%$q_value%" },
-                                  %options,
-                                }
-                              );
-      push @cust_main, qsearch( 'cust_main',
-                                { 'ship_company' => { 'op' => 'ILIKE',
-                                                   'value' => "%$q_value%" },
-                                  %options,
-                                }
-                              )
+      push @cust_main, qsearch( {
+        'table'     => 'cust_main',
+        'hashref'   => { 'company'  => { 'op'    => 'ILIKE',
+                                         'value' => "%$q_value%" },
+                         %options,
+                       },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualization
+      } );
+      push @cust_main, qsearch(  {
+        'table'     => 'cust_main',
+        'hashref'   => { 'ship_company' => { 'op'    => 'ILIKE',
+                                             'value' => "%$q_value%" },
+                         %options,
+                       },
+        'extra_sql' => " AND $agentnums_sql", #agent virtualization
+      } )
         if defined dbdef->table('cust_main')->column('ship_last');
 
       #fuzzy
       push @cust_main, FS::cust_main->fuzzy_search(
-        { 'last'     => $value },
-        \%options,
+        { 'last'     => $value }, #fuzzy hashref
+        \%options,                #hashref
+        '',                       #select
+        " AND $agentnums_sql",    #extra_sql  #agent virtualization
       );
       push @cust_main, FS::cust_main->fuzzy_search(
-        { 'company'  => $value },
-        \%options,
+        { 'company'  => $value }, #fuzzy hashref
+        \%options,                #hashref
+        '',                       #select
+        " AND $agentnums_sql",    #extra_sql  #agent virtualization
       );
 
     }
index de4d047..aa39d89 100644 (file)
@@ -608,7 +608,8 @@ sub freq_pretty {
   my $self = shift;
   my $freq = $self->freq;
 
-  my $freqs_href = $self->freqs_href;
+  #my $freqs_href = $self->freqs_href;
+  my $freqs_href = freqs_href();
 
   if ( exists($freqs_href->{$freq}) ) {
     $freqs_href->{$freq};
index 098fe4a..6db8271 100644 (file)
@@ -345,3 +345,4 @@ t/pay_batch.t
 FS/ConfDefaults.pm
 t/ConfDefaults.t
 FS/m2name_Common.pm
+FS/CurrentUser.pm
index aa480a5..78ec25f 100644 (file)
@@ -11,6 +11,8 @@
                  'value'       => $agentnum,
                  'empty_label' => 'all',
                  'hashref'     => { 'disabled' => '' },
+                 'extra_sql'   => ' AND '.
+                                  $FS::CurrentUser::CurrentUser->agentnums_sql,
                  %select_opt,
              )
 %>
index 96f0177..6c8089b 100644 (file)
@@ -10,6 +10,7 @@
   ##opt
   # 'empty_label' => '', #better specify it though, the default might change
   # 'hashref'     => {},
+  # 'extra_sql'   => '',
   # 'records'     => \@records, #instead of hashref
   # 'pre_options' => [ 'value' => 'option' ], #before normal options
 
   if ( $opt{'records'} ) {
     @records = @{ $opt{'records'} };
   } else {
-    @records = qsearch( $opt{'table'}, ( $opt{'hashref'} || {} ) );
+    @records = qsearch( {
+      'table'     => $opt{'table'},
+      'hashref'   => ( $opt{'hashref'} || {} ),
+      'extra_sql' => ( $opt{'extra_sql'} || '' ),
+    });
   }
 
   my @pre_options = $opt{'pre_options'} ? @{ $opt{'pre_options'} } : ();
index 2227262..83c8d17 100644 (file)
@@ -3,9 +3,16 @@
 
   my @agents;
   if ( $opt{'agents'} ) {
-    @agents = @{ $opt{'agents'} };
+    #@agents = @{ $opt{'agents'} };
+    #here is the agent virtualization
+    my $agentnums_href = $FS::CurrentUser::CurrentUser->agentnums_href;
+    @agents = grep $agentnums_href->{$_->agentnum}, @{ $opt{'agents'} };
   } else {
-    @agents = qsearch( 'agent', { disabled=>'' } );
+    @agents = qsearch( {
+      'table'     => 'agent',
+      'hashref'   => { disabled=>'' },
+      'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+    });
   }
 
 %>
index 2108653..16128d5 100755 (executable)
@@ -1,4 +1,9 @@
 <%
+
+   my $join_cust_main = 'LEFT JOIN cust_main USING ( custnum )';
+   #here is the agent virtualization
+   my $agentnums_sql = $FS::CurrentUser::CurrentUser->agentnums_sql;
+
    my( $count_query, $sql_query );
    my( $count_addl ) = ( '' );
    my( $distinct ) = ( '' );
    my($agentnum) = ( '' );
    my($open, $days) = ( '', '' );
    if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) {
-     $count_query = "SELECT COUNT(*) FROM cust_bill WHERE invnum = $2";
+     $count_query =
+       "SELECT COUNT(*) FROM cust_bill $join_cust_main".
+       "  WHERE invnum = $2 AND $agentnums_sql"; #agent virtualization
      $sql_query = {
        'table'     => 'cust_bill',
+       'addl_from' => $join_cust_main,
        'hashref'   => { 'invnum' => $2 },
        #'select'    => '*',
+       'extra_sql' => " AND $agentnums_sql", #agent virtualization
      };
    } else {
    #if ( $cgi->param('begin') || $cgi->param('end')
        push @where, "cust_bill._date < ". (time-86400*$days) if $days;
      }
 
+     #here is the agent virtualization
+     push @where, $agentnums_sql;
      my $extra_sql = scalar(@where) ? 'WHERE '. join(' AND ', @where) : '';
 
-     my $addl_from = 'left join cust_main using ( custnum )';
-
      if ( $cgi->param('newest_percust') ) {
        $distinct = 'DISTINCT ON ( cust_bill.custnum )';
        $orderby = 'ORDER BY cust_bill.custnum ASC, cust_bill._date DESC';
                        '$%.2f total outstanding balance',
                      ];
      }
-     $count_query .=  " FROM cust_bill $addl_from $extra_sql";
+     $count_query .=  " FROM cust_bill $join_cust_main $extra_sql";
    
      $sql_query = {
        'table'     => 'cust_bill',
-       'addl_from' => $addl_from,
+       'addl_from' => $join_cust_main,
        'hashref'   => {},
        'select'    => "$distinct ". join(', ',
                         'cust_bill.*',
index 733e5dc..5b7ccec 100755 (executable)
@@ -117,6 +117,10 @@ if ( $cgi->param('browse')
 
   my $addl_qual = join(' AND ', @qual);
 
+  #here is the agent virtualization
+  $addl_qual .= ( $addl_qual ? ' AND ' : '' ).
+                $FS::CurrentUser::CurrentUser->agentnums_sql;
+
   if ( $addl_qual ) {
     $qual .= ' AND ' if $qual;
     $qual .= $addl_qual;
@@ -322,32 +326,43 @@ END
 
   print "<BR><BR>". $pager. include('/elements/table-grid.html'). <<END;
       <TR>
-        <TH></TH>
-        <TH>(bill) name</TH>
-        <TH>company</TH>
+        <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
+        <TH CLASS="grid" BGCOLOR="#cccccc">(bill) name</TH>
+        <TH CLASS="grid" BGCOLOR="#cccccc">company</TH>
 END
 
 if ( defined dbdef->table('cust_main')->column('ship_last') ) {
   print <<END;
-      <TH>(service) name</TH>
-      <TH>company</TH>
+      <TH CLASS="grid" BGCOLOR="#cccccc">(service) name</TH>
+      <TH CLASS="grid" BGCOLOR="#cccccc">company</TH>
 END
 }
 
 foreach my $addl_header ( @addl_headers ) {
-  print "<TH>$addl_header</TH>";
+  print '<TH CLASS="grid" BGCOLOR="#cccccc">'. "$addl_header</TH>";
 }
 
 print <<END;
-        <TH>Packages</TH>
-        <TH COLSPAN=2>Services</TH>
+        <TH CLASS="grid" BGCOLOR="#cccccc">Packages</TH>
+        <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Services</TH>
       </TR>
 END
 
+  my $bgcolor1 = '#eeeeee';
+  my $bgcolor2 = '#ffffff';
+  my $bgcolor;
+
   my(%saw,$cust_main);
   foreach $cust_main (
     sort $sortby grep(!$saw{$_->custnum}++, @cust_main)
   ) {
+
+    if ( $bgcolor eq $bgcolor1 ) {
+      $bgcolor = $bgcolor2;
+    } else {
+      $bgcolor = $bgcolor1;
+    }
+
     my($custnum,$last,$first,$company)=(
       $cust_main->custnum,
       $cust_main->getfield('last'),
@@ -377,9 +392,9 @@ END
     %>
 
     <TR>
-      <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= $custnum %></FONT></A></TD>
-      <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$last, $first" %></FONT></A></TD>
-      <TD ROWSPAN=<%= $rowspan || 1 %>><%= $pcompany %></TD>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= $custnum %></FONT></A></TD>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$last, $first" %></FONT></A></TD>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %>><%= $pcompany %></TD>
 
     <%
     if ( defined dbdef->table('cust_main')->column('ship_last') ) {
@@ -393,14 +408,14 @@ END
         : '<FONT SIZE=-1>&nbsp;</FONT>';
       %>
 
-      <TD ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$ship_last, $ship_first" %></FONT></A></TD>
-      <TD ROWSPAN=<%= $rowspan || 1 %>><%= $pship_company %></A></TD>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %>><A HREF="<%= $view %>"><FONT SIZE=-1><%= "$ship_last, $ship_first" %></FONT></A></TD>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %>><%= $pship_company %></A></TD>
 
     <% }
 
     foreach my $addl_col ( @addl_cols ) { %>
 
-      <TD ROWSPAN=<%= $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
+      <TD CLASS="grid" BGCOLOR="<%= $bgcolor %>" ROWSPAN=<%= $rowspan || 1 %> ALIGN=right><FONT SIZE=-1>
 
       <% if ( $addl_col eq 'tickets' ) {
         if ( @custom_priorities ) {
@@ -457,14 +472,14 @@ END
       #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } );
       my $rowspan = scalar(@cust_svc) || 1;
 
-      print $n1, qq!<TD ROWSPAN=$rowspan><A HREF="$pkgview"><FONT SIZE=-1>$pkg - $comment</FONT></A></TD>!;
+      print $n1, qq!<TD CLASS="grid" BGCOLOR="$bgcolor"  ROWSPAN=$rowspan><A HREF="$pkgview"><FONT SIZE=-1>$pkg - $comment</FONT></A></TD>!;
       my($n2)='';
       foreach my $cust_svc ( @cust_svc ) {
          my($label, $value, $svcdb) = $cust_svc->label;
          my($svcnum) = $cust_svc->svcnum;
          my($sview) = $p.'view';
-         print $n2,qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$label</FONT></A></TD>!,
-               qq!<TD><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$value</FONT></A></TD>!;
+         print $n2,qq!<TD CLASS="grid" BGCOLOR="$bgcolor" ><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$label</FONT></A></TD>!,
+               qq!<TD CLASS="grid" BGCOLOR="$bgcolor" ><A HREF="$sview/$svcdb.cgi?$svcnum"><FONT SIZE=-1>$value</FONT></A></TD>!;
          $n2="</TR><TR>";
       }
       #print qq!</TR><TR>\n!;
index a2fb89c..e8b3f49 100755 (executable)
@@ -1,19 +1,80 @@
 <%
 
-my %part_pkg = map { $_->pkgpart => $_ } qsearch('part_pkg', {});
+my %part_pkg = map { $_->pkgpart => $_ } qsearch('part_pkg', {});
 
 my($query) = $cgi->keywords;
 
-my $orderby;
-my @where;
-my $cjoin = '';
+my @where = ();
+
+##
+# parse agent
+##
 
 if ( $cgi->param('agentnum') =~ /^(\d+)$/ and $1 ) {
-  $cjoin = "LEFT JOIN cust_main USING ( custnum )";
   push @where,
     "agentnum = $1";
 }
 
+##
+# parse status
+##
+
+if (    $cgi->param('magic')  eq 'active'
+     || $cgi->param('status') eq 'active' ) {
+
+  push @where, FS::cust_pkg->active_sql();
+
+} elsif (    $cgi->param('magic')  eq 'suspended'
+          || $cgi->param('status') eq 'suspended'  ) {
+
+  push @where, FS::cust_pkg->suspended_sql();
+
+} elsif (    $cgi->param('magic')  =~ /^cancell?ed$/
+          || $cgi->param('status') =~ /^cancell?ed$/ ) {
+
+  push @where, FS::cust_pkg->cancelled_sql();
+
+} elsif ( $cgi->param('status') =~ /^(one-time charge|inactive)$/ ) {
+
+  push @where, FS::cust_pkg->inactive_sql();
+
+}
+
+###
+# parse package class
+###
+
+#false lazinessish w/graph/cust_bill_pkg.cgi
+my $classnum = 0;
+my @pkg_class = ();
+if ( $cgi->param('classnum') =~ /^(\d*)$/ ) {
+  $classnum = $1;
+  if ( $classnum ) { #a specific class
+    push @where, "classnum = $classnum";
+
+    #@pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) );
+    #die "classnum $classnum not found!" unless $pkg_class[0];
+    #$title .= $pkg_class[0]->classname.' ';
+
+  } elsif ( $classnum eq '' ) { #the empty class
+
+    push @where, "classnum IS NULL";
+    #$title .= 'Empty class ';
+    #@pkg_class = ( '(empty class)' );
+  } elsif ( $classnum eq '0' ) {
+    #@pkg_class = qsearch('pkg_class', {} ); # { 'disabled' => '' } );
+    #push @pkg_class, '(empty class)';
+  } else {
+    die "illegal classnum";
+  }
+}
+#eslaf
+
+###
+# parse magic, legacy, etc.
+###
+
+my $orderby;
 if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) {
   $orderby = 'ORDER BY bill';
 
@@ -23,7 +84,8 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) {
     #"bill <= $ending",
     "CASE WHEN bill IS NULL THEN 0 ELSE bill END >= $beginning ",
     "CASE WHEN bill IS NULL THEN 0 ELSE bill END <= $ending",
-    '( cancel IS NULL OR cancel = 0 )';
+    #'( cancel IS NULL OR cancel = 0 )'
+  ;
 
 } else {
 
@@ -33,30 +95,6 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) {
 
     $orderby = 'ORDER BY pkgnum';
 
-    if ( $cgi->param('magic') eq 'active' ) {
-
-      #push @where,
-      #  '( susp IS NULL OR susp = 0 )',
-      #  '( cancel IS NULL OR cancel = 0)';
-      push @where, FS::cust_pkg->active_sql();
-
-    } elsif ( $cgi->param('magic') eq 'suspended' ) {
-
-      push @where,
-        'susp IS NOT NULL',
-        'susp != 0',
-        '( cancel IS NULL OR cancel = 0)';
-
-    } elsif ( $cgi->param('magic') =~ /^cancell?ed$/ ) {
-
-      push @where,
-        'cancel IS NOT NULL',
-        'cancel != 0';
-
-    } else {
-      die "guru meditation #420";
-    }
-
     if ( $cgi->param('pkgpart') =~ /^(\d+)$/ ) {
       push @where, "pkgpart = $1";
     }
@@ -84,21 +122,35 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) {
 
 }
 
+##
+# setup queries, links, subs, etc. for the search
+##
+
+# here is the agent virtualization
+push @where, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
 my $extra_sql = scalar(@where) ? ' WHERE '. join(' AND ', @where) : '';
 
-my $count_query = "SELECT COUNT(*) FROM cust_pkg $cjoin $extra_sql";
+my $addl_from = 'LEFT JOIN cust_main USING ( custnum  ) '.
+                'LEFT JOIN part_pkg  USING ( pkgpart  ) '.
+                'LEFT JOIN pkg_class USING ( classnum ) ';
+
+my $count_query = "SELECT COUNT(*) FROM cust_pkg $addl_from $extra_sql";
 
 my $sql_query = {
   'table'     => 'cust_pkg',
   'hashref'   => {},
   'select'    => join(', ',
                             'cust_pkg.*',
+                            ( map "part_pkg.$_", qw( pkg freq ) ),
+                            'pkg_class.classname',
                             'cust_main.custnum as cust_main_custnum',
-                            FS::UI::Web::cust_sql_fields(),
+                            FS::UI::Web::cust_sql_fields(
+                              $cgi->param('cust_fields')
+                            ),
                  ),
   'extra_sql' => "$extra_sql $orderby",
-  'addl_from' => ' LEFT JOIN cust_main USING ( custnum ) ',
-                 #' LEFT JOIN part_pkg  USING ( pkgpart ) '
+  'addl_from' => $addl_from,
 };
 
 my $link = sub {
@@ -138,6 +190,10 @@ sub time_or_blank {
    };
 }
 
+###
+# and finally, include the search template
+### 
+
 %><%=  include( 'elements/search.html',
                   'title'       => 'Package Search Results', 
                   'name'        => 'packages',
@@ -146,6 +202,7 @@ sub time_or_blank {
                   #'redirect'    => $link,
                   'header'      => [ '#',
                                      'Package',
+                                     'Class',
                                      'Status',
                                      'Freq.',
                                      'Setup',
@@ -154,18 +211,25 @@ sub time_or_blank {
                                      'Susp.',
                                      'Expire',
                                      'Cancel',
-                                     FS::UI::Web::cust_header(),
+                                     FS::UI::Web::cust_header(
+                                       $cgi->param('cust_fields')
+                                     ),
                                      'Services',
                                    ],
                   'fields'      => [
                     'pkgnum',
-                    sub { my $part_pkg = $part_pkg{shift->pkgpart};
-                          $part_pkg->pkg; # ' - '. $part_pkg->comment;
+                    sub { #my $part_pkg = $part_pkg{shift->pkgpart};
+                          #$part_pkg->pkg; # ' - '. $part_pkg->comment;
+                          $_[0]->pkg; # ' - '. $_[0]->comment;
                         },
+                    'classname',
                     sub { ucfirst(shift->status); },
                     sub { #shift->part_pkg->freq_pretty;
-                          my $part_pkg = $part_pkg{shift->pkgpart};
-                          $part_pkg->freq_pretty;
+
+                          #my $part_pkg = $part_pkg{shift->pkgpart};
+                          #$part_pkg->freq_pretty;
+
+                          FS::part_pkg::freq_pretty(shift);
                         },
 
                     #sub { time2str('%b %d %Y', shift->setup); },
@@ -204,6 +268,7 @@ sub time_or_blank {
                   'color' => [
                     '',
                     '',
+                    '',
                     sub { shift->statuscolor; },
                     '',
                     '',
@@ -212,12 +277,16 @@ sub time_or_blank {
                     '',
                     '',
                     '',
-                    ( map { '' } FS::UI::Web::cust_header() ),
+                    ( map { '' }
+                          FS::UI::Web::cust_header(
+                                                    $cgi->param('cust_fields')
+                                                  )
+                    ),
                     '',
                   ],
-                  'style' => [ '', '', 'b' ],
-                  'size'  => [ '', '', '-1', ],
-                  'align' => 'rlclrrrrrr',
+                  'style' => [ '', '', '', 'b' ],
+                  'size'  => [ '', '', '', '-1', ],
+                  'align' => 'rllclrrrrrr',
                   'links' => [
                     $link,
                     $link,
@@ -229,7 +298,12 @@ sub time_or_blank {
                     '',
                     '',
                     '',
-                    ( map { $clink } FS::UI::Web::cust_header() ),
+                    '',
+                    ( map { $clink }
+                          FS::UI::Web::cust_header(
+                                                    $cgi->param('cust_fields')
+                                                  )
+                    ),
                     '',
                   ],
               )
index b145919..ef68ba0 100755 (executable)
@@ -5,11 +5,9 @@ my $orderby = 'ORDER BY svcnum';
 my($query)=$cgi->keywords;
 $query ||= ''; #to avoid use of unitialized value errors
 
-my $cjoin = '';
 my @extra_sql = ();
 if ( $query =~ /^UN_(.*)$/ ) {
   $query = $1;
-  $cjoin = 'LEFT JOIN cust_svc USING ( svcnum )';
   push @extra_sql, 'pkgnum IS NULL';
 }
 
@@ -24,7 +22,6 @@ if ( $query eq 'svcnum' ) {
   push @extra_sql, "popnum = $1";
   $orderby = "ORDER BY LOWER(username)";
 } elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
-  $cjoin ||= 'LEFT JOIN cust_svc USING ( svcnum )';
   push @extra_sql, "svcpart = $1";
   $orderby = "ORDER BY uid";
   #$orderby = "ORDER BY svcnum";
@@ -72,12 +69,20 @@ if ( $query eq 'svcnum' ) {
 
 }
 
+my $addl_from = ' LEFT JOIN cust_svc  USING ( svcnum  ) '.
+                ' LEFT JOIN part_svc  USING ( svcpart ) '.
+                ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
+                ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
 my $extra_sql = 
   scalar(@extra_sql)
     ? ' WHERE '. join(' AND ', @extra_sql )
     : '';
 
-my $count_query = "SELECT COUNT(*) FROM svc_acct $cjoin $extra_sql";
+my $count_query = "SELECT COUNT(*) FROM svc_acct $addl_from $extra_sql";
 #if ( keys %svc_acct ) {
 #  $count_query .= ' WHERE '.
 #                    join(' AND ', map "$_ = ". dbh->quote($svc_acct{$_}),
@@ -94,10 +99,7 @@ my $sql_query = {
                     FS::UI::Web::cust_sql_fields(),
                   ),
   'extra_sql' => "$extra_sql $orderby",
-  'addl_from' => ' LEFT JOIN cust_svc  USING ( svcnum  ) '.
-                 ' LEFT JOIN part_svc  USING ( svcpart ) '.
-                 ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
-                 ' LEFT JOIN cust_main USING ( custnum ) ',
+  'addl_from' => $addl_from,
 };
 
 my $link      = [ "${p}view/svc_acct.cgi?",   'svcnum'  ];
@@ -117,21 +119,21 @@ my $link_cust = sub {
                  'count_query' => $count_query,
                  'redirect'    => $link,
                  'header'      => [ '#',
+                                    'Service',
                                     'Account',
                                     'UID',
-                                    'Service',
                                     FS::UI::Web::cust_header(),
                                   ],
                  'fields'      => [ 'svcnum',
+                                    'svc',
                                     'email',
                                     'uid',
-                                    'svc',
                                     \&FS::UI::Web::cust_fields,
                                   ],
                  'links'       => [ $link,
                                     $link,
                                     $link,
-                                    '',
+                                    $link,
                                     ( map { $link_cust }
                                           FS::UI::Web::cust_header()
                                     ),
index b02eea8..1eda373 100755 (executable)
@@ -6,38 +6,50 @@ my($query)=$cgi->keywords;
 $query ||= ''; #to avoid use of unitialized value errors
 
 my $orderby = 'ORDER BY svcnum';
-my $join = '';
 my %svc_domain = ();
-my $extra_sql = '';
+my @extra_sql = ();
 if ( $query eq 'svcnum' ) {
   #$orderby = 'ORDER BY svcnum';
 } elsif ( $query eq 'domain' ) {
   $orderby = 'ORDER BY domain';
-} elsif ( $query eq 'UN_svcnum' ) {
+} elsif ( $query eq 'UN_svcnum' ) { #UN searches need to be acl'ed (and need to
+                                    #fix $agentnums_sql
   #$orderby = 'ORDER BY svcnum';
-  $join = 'LEFT JOIN cust_svc USING ( svcnum )';
-  $extra_sql = ' WHERE pkgnum IS NULL';
-} elsif ( $query eq 'UN_domain' ) {
+  push @extra_sql, 'pkgnum IS NULL';
+} elsif ( $query eq 'UN_domain' ) { #UN searches need to be acl'ed (and need to
+                                    #fix $agentnums_sql
   $orderby = 'ORDER BY domain';
-  $join = 'LEFT JOIN cust_svc USING ( svcnum )';
-  $extra_sql = ' WHERE pkgnum IS NULL';
+  push @extra_sql, 'pkgnum IS NULL';
 } elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
   #$orderby = 'ORDER BY svcnum';
-  $join = 'LEFT JOIN cust_svc USING ( svcnum )';
-  $extra_sql = " WHERE svcpart = $1";
+  push @extra_sql, "svcpart = $1";
 } else {
   $cgi->param('domain') =~ /^([\w\-\.]+)$/; 
-  $join = '';
   $svc_domain{'domain'} = $1;
 }
 
-my $count_query = "SELECT COUNT(*) FROM svc_domain $join $extra_sql";
+my $addl_from = ' LEFT JOIN cust_svc  USING ( svcnum  ) '.
+                ' LEFT JOIN part_svc  USING ( svcpart ) '.
+                ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
+                ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $extra_sql = '';
+if ( @extra_sql ) {
+  $extra_sql = ( keys(%svc_domain) ? ' AND ' : ' WHERE ' ).
+               join(' AND ', @extra_sql );
+}
+
+my $count_query = "SELECT COUNT(*) FROM svc_domain $addl_from ";
 if ( keys %svc_domain ) {
   $count_query .= ' WHERE '.
                     join(' AND ', map "$_ = ". dbh->quote($svc_domain{$_}),
                                       keys %svc_domain
                         );
 }
+$count_query .= $extra_sql;
 
 my $sql_query = {
   'table'     => 'svc_domain',
@@ -48,9 +60,7 @@ my $sql_query = {
                     FS::UI::Web::cust_sql_fields(),
                  ),
   'extra_sql' => "$extra_sql $orderby",
-  'addl_from' => 'LEFT JOIN cust_svc  USING ( svcnum  ) '.
-                 'LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
-                 'LEFT JOIN cust_main USING ( custnum ) ',
+  'addl_from' => $addl_from,
 };
 
 my $link = [ "${p}view/svc_domain.cgi?", 'svcnum' ];
@@ -68,15 +78,18 @@ my $link_cust = sub {
                  'count_query'       => $count_query,
                  'redirect'          => $link,
                  'header'            => [ '#',
+                                          'Service',
                                           'Domain',
                                           FS::UI::Web::cust_header(),
                                         ],
                  'fields'            => [ 'svcnum',
+                                          'svc',
                                           'domain',
                                           \&FS::UI::Web::cust_fields,
                                         ],
                  'links'             => [ $link,
                                           $link,
+                                          $link,
                                           ( map { $link_cust }
                                                 FS::UI::Web::cust_header()
                                           ),
index a204e34..4961967 100755 (executable)
@@ -5,14 +5,12 @@ my $conf = new FS::Conf;
 my($query)=$cgi->keywords;
 $query ||= ''; #to avoid use of unitialized value errors
 
-
 my $orderby;
 
-my $cjoin = '';
 my @extra_sql = ();
-if ( $query =~ /^UN_(.*)$/ ) {
+if ( $query =~ /^UN_(.*)$/ ) { #UN searches need to be acl'ed (and need to
+                                    #fix $agentnums_sql
   $query = $1;
-  $cjoin = 'LEFT JOIN cust_svc USING ( svcnum )';
   push @extra_sql, 'pkgnum IS NULL';
 }
 
@@ -22,12 +20,20 @@ if ( $query eq 'svcnum' ) {
   eidiot('unimplemented');
 }
 
+my $addl_from = ' LEFT JOIN cust_svc  USING ( svcnum  ) '.
+                ' LEFT JOIN part_svc  USING ( svcpart ) '.
+                ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
+                ' LEFT JOIN cust_main USING ( custnum ) ';
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
 my $extra_sql = 
   scalar(@extra_sql)
     ? ' WHERE '. join(' AND ', @extra_sql )
     : '';
 
-my $count_query = "SELECT COUNT(*) FROM svc_forward $cjoin $extra_sql";
+my $count_query = "SELECT COUNT(*) FROM svc_forward $addl_from $extra_sql";
 my $sql_query = {
   'table'     => 'svc_forward',
   'hashref'   => {},
@@ -37,10 +43,7 @@ my $sql_query = {
                     FS::UI::Web::cust_sql_fields(),
                  ),
   'extra_sql' => "$extra_sql $orderby",
-  'addl_from' => ' LEFT JOIN cust_svc  USING ( svcnum  ) '.
-                 ' LEFT JOIN part_svc  USING ( svcpart ) '.
-                 ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '.
-                 ' LEFT JOIN cust_main USING ( custnum ) ',
+  'addl_from' => $addl_from,
 };
 
 #        <TH>Service #<BR><FONT SIZE=-1>(click to view forward)</FONT></TH>
@@ -100,16 +103,19 @@ my $link_cust = sub {
                  'count_query'       => $count_query,
                  'redirect'          => $link,
                  'header'            => [ '#',
+                                          'Service',
                                           'Mail to',
                                           'Forwards to',
                                           FS::UI::Web::cust_header(),
                                         ],
                  'fields'            => [ 'svcnum',
+                                          'svc',
                                           $format_src,
                                           $format_dst,
                                           \&FS::UI::Web::cust_fields,
                                         ],
                  'links'             => [ $link,
+                                          $link,
                                           $link_src,
                                           $link_dst,
                                           ( map { $link_cust }