diff options
author | ivan <ivan> | 2006-06-19 02:33:52 +0000 |
---|---|---|
committer | ivan <ivan> | 2006-06-19 02:33:52 +0000 |
commit | c0e8da2f1e89729efa1032241e4239765a296514 (patch) | |
tree | 68af748a3dc0c983e3ed255ad1bc05e9bf2eb8e7 /FS | |
parent | bb65b62d3d68ac0788f230fe2e35ebe1913fc0f5 (diff) |
agent virtualization, take one (stuff from "inactive" changeset snuck into cust_main.pm and the package reporting changeset in search/cust_pkg.cgi here too)
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/CurrentUser.pm | 50 | ||||
-rw-r--r-- | FS/FS/Record.pm | 29 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 13 | ||||
-rw-r--r-- | FS/FS/UID.pm | 19 | ||||
-rw-r--r-- | FS/FS/access_user.pm | 51 | ||||
-rw-r--r-- | FS/FS/cust_main.pm | 213 | ||||
-rw-r--r-- | FS/FS/part_pkg.pm | 3 | ||||
-rw-r--r-- | FS/MANIFEST | 1 |
8 files changed, 277 insertions, 102 deletions
diff --git a/FS/FS/CurrentUser.pm b/FS/FS/CurrentUser.pm new file mode 100644 index 000000000..13d34167d --- /dev/null +++ b/FS/FS/CurrentUser.pm @@ -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; + diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 67de071c6..fa0d2d87e 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -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'); } diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 17d541e8c..7219274e6 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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 diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm index 83b652b0f..21df9441b 100644 --- a/FS/FS/UID.pm +++ b/FS/FS/UID.pm @@ -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"; diff --git a/FS/FS/access_user.pm b/FS/FS/access_user.pm index ca311d3b8..c95d02984 100644 --- a/FS/FS/access_user.pm +++ b/FS/FS/access_user.pm @@ -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. diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 2763c1386..8956d5b26 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -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 ); } diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index de4d047de..aa39d890b 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -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}; diff --git a/FS/MANIFEST b/FS/MANIFEST index 098fe4a64..6db82710c 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -345,3 +345,4 @@ t/pay_batch.t FS/ConfDefaults.pm t/ConfDefaults.t FS/m2name_Common.pm +FS/CurrentUser.pm |