summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Conf.pm8
-rw-r--r--FS/FS/Record.pm4
-rw-r--r--FS/FS/cust_main.pm177
-rw-r--r--FS/FS/cust_svc.pm6
-rw-r--r--FS/FS/part_export.pm30
-rw-r--r--FS/FS/part_export/cp.pm56
-rw-r--r--FS/FS/part_export/forward_shellcommands.pm24
-rw-r--r--FS/FS/part_export/shellcommands.pm14
-rw-r--r--FS/FS/part_export/sqlmail.pm3
-rw-r--r--FS/FS/part_export/sqlradius.pm23
-rw-r--r--FS/FS/part_export/sqlradius_withdomain.pm12
-rw-r--r--FS/FS/part_pkg.pm11
-rw-r--r--FS/FS/svc_acct.pm17
-rw-r--r--FS/FS/svc_acct_pop.pm13
-rw-r--r--FS/FS/svc_domain.pm2
-rw-r--r--FS/MANIFEST30
-rwxr-xr-xFS/bin/freeside-overdue196
-rwxr-xr-xFS/bin/freeside-setup6
-rwxr-xr-xFS/bin/freeside-sqlradius-reset4
-rw-r--r--FS/t/part_export-sqlradius_withdomain.t5
20 files changed, 302 insertions, 339 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 84b3c26ed..706ebe720 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -1032,6 +1032,14 @@ httemplate/docs/config.html
},
{
+ 'key' => 'radius-ip',
+ 'section' => '',
+ 'description' => 'RADIUS attribute for IP addresses.',
+ 'type' => 'select',
+ 'select_enum' => [ 'Framed-IP-Address', 'Framed-Address' ],
+ },
+
+ {
'key' => 'svc_acct-alldomains',
'section' => '',
'description' => 'Allow accounts to select any domain in the database. Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index 9a724feac..02fd4e390 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -426,11 +426,11 @@ sub AUTOLOAD {
$field =~ s/.*://;
if ( defined($value) ) {
confess "errant AUTOLOAD $field for $self (arg $value)"
- unless $self->can('setfield');
+ unless ref($self) && $self->can('setfield');
$self->setfield($field,$value);
} else {
confess "errant AUTOLOAD $field for $self (no args)"
- unless $self->can('getfield');
+ unless ref($self) && $self->can('getfield');
$self->getfield($field);
}
}
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 7e2ff388e..4302c504a 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -4,7 +4,12 @@ use strict;
use vars qw( @ISA $conf $Debug $import );
use Safe;
use Carp;
-use Time::Local qw(timelocal_nocheck);
+BEGIN {
+ eval "use Time::Local;";
+ die "Time::Local version 1.05 required with Perl versions before 5.6"
+ if $] < 5.006 && !defined($Time::Local::VERSION);
+ eval "use Time::Local qw(timelocal timelocal_nocheck);";
+}
use Date::Format;
#use Date::Manip;
use Business::CreditCard;
@@ -301,23 +306,11 @@ sub insert {
}
}
- #false laziness with sub replace
- my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ $error = $self->queue_fuzzyfiles_update;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return "queueing job (transaction rolled back): $error";
- }
-
- if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) {
- $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "queueing job (transaction rolled back): $error";
- }
+ return "updating fuzzy search cache: $error";
}
- #eslaf
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -520,34 +513,47 @@ sub replace {
if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ &&
grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) {
- # card/check info has changed, want to retry realtime_card invoice events
- #false laziness w/collect
- foreach my $cust_bill_event (
- grep {
- #$_->part_bill_event->plan eq 'realtime-card'
- $_->part_bill_event->eventcode =~
- /^\$cust_bill\->realtime_(card|ach|lec)\(\);$/
- && $_->status eq 'done'
- && $_->statustext
- }
- map { $_->cust_bill_event }
- grep { $_->cust_bill_event }
- $self->open_cust_bill
-
- ) {
- my $error = $cust_bill_event->retry;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error scheduling invoice events for retry: $error";
- }
+ # card/check/lec info has changed, want to retry realtime_ invoice events
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
- #eslaf
+ }
+ $error = $self->queue_fuzzyfiles_update;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "updating fuzzy search cache: $error";
}
- #false laziness with sub insert
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=item queue_fuzzyfiles_update
+
+Used by insert & replace to update the fuzzy search cache
+
+=cut
+
+sub queue_fuzzyfiles_update {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ my $error = $queue->insert($self->getfield('last'), $self->company);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
@@ -555,13 +561,12 @@ sub replace {
if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) {
$queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
- $error = $queue->insert($self->getfield('last'), $self->company);
+ $error = $queue->insert($self->getfield('ship_last'), $self->ship_company);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
}
}
- #eslaf
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -1271,7 +1276,10 @@ invoice_time - Use this time when deciding when to print invoices and
late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse>
for conversion functions.
-retry_card - Retry cards even when not scheduled by invoice events.
+retry - Retry card/echeck/LEC transactions even when not scheduled by invoice
+events.
+
+retry_card - Deprecated alias for 'retry'
batch_card - This option is deprecated. See the invoice events web interface
to control whether cards are batched or run against a realtime gateway.
@@ -1305,26 +1313,16 @@ sub collect {
return '';
}
- if ( exists($options{'retry_card'}) && $options{'retry_card'} ) {
- #false laziness w/replace
- foreach my $cust_bill_event (
- grep {
- #$_->part_bill_event->plan eq 'realtime-card'
- $_->part_bill_event->eventcode eq '$cust_bill->realtime_card();'
- && $_->status eq 'done'
- && $_->statustext
- }
- map { $_->cust_bill_event }
- grep { $_->cust_bill_event }
- $self->open_cust_bill
- ) {
- my $error = $cust_bill_event->retry;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error scheduling invoice events for retry: $error";
- }
+ if ( exists($options{'retry_card'}) ) {
+ carp 'retry_card option passed to collect is deprecated; use retry';
+ $options{'retry'} ||= $options{'retry_card'};
+ }
+ if ( exists($options{'retry'}) && $options{'retry'} ) {
+ my $error = $self->retry_realtime;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
- #eslaf
}
foreach my $cust_bill ( $self->cust_bill ) {
@@ -1414,6 +1412,60 @@ sub collect {
}
+=item retry_realtime
+
+Schedules realtime credit card / electronic check / LEC billing events for
+for retry. Useful if card information has changed or manual retry is desired.
+The 'collect' method must be called to actually retry the transaction.
+
+Implementation details: For each of this customer's open invoices, changes
+the status of the first "done" (with statustext error) realtime processing
+event to "failed".
+
+=cut
+
+sub retry_realtime {
+ my $self = shift;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ foreach my $cust_bill (
+ grep { $_->cust_bill_event }
+ $self->open_cust_bill
+ ) {
+ my @cust_bill_event =
+ sort { $a->part_bill_event->seconds <=> $b->part_bill_event->seconds }
+ grep {
+ #$_->part_bill_event->plan eq 'realtime-card'
+ $_->part_bill_event->eventcode =~
+ /\$cust_bill\->realtime_(card|ach|lec)$/
+ && $_->status eq 'done'
+ && $_->statustext
+ }
+ $cust_bill->cust_bill_event;
+ next unless @cust_bill_event;
+ my $error = $cust_bill_event[0]->retry;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error scheduling invoice event for retry: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
=item realtime_bop METHOD AMOUNT [ OPTION => VALUE ... ]
Runs a realtime credit card, ACH (electronic check) or phone bill transaction
@@ -1504,6 +1556,10 @@ sub realtime_bop {
( $content{account_number}, $content{routing_code} ) =
split('@', $self->payinfo);
$content{bank_name} = $self->payname;
+ $content{account_type} = 'CHECKING';
+ $content{account_name} = $payname;
+ $content{customer_org} = $self->company ? 'B' : 'I';
+ $content{customer_ssn} = $self->ss;
} elsif ( $method eq 'LEC' ) {
$content{phone} = $self->payinfo;
}
@@ -2501,4 +2557,3 @@ L<FS::cust_main_invoice>, L<FS::UID>, schema.html from the base documentation.
1;
-
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index 8ac806519..c0cb6f4e9 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -1,7 +1,7 @@
package FS::cust_svc;
use strict;
-use vars qw( @ISA );
+use vars qw( @ISA $ignore_quantity );
use Carp qw( cluck );
use FS::Record qw( qsearch qsearchs dbh );
use FS::cust_pkg;
@@ -17,6 +17,8 @@ use FS::part_export;
@ISA = qw( FS::Record );
+$ignore_quantity = 0;
+
sub _cache {
my $self = shift;
my ( $hashref, $cache ) = @_;
@@ -229,7 +231,7 @@ sub check {
});
return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
" services for pkgnum ". $self->pkgnum
- if scalar(@cust_svc) >= $quantity;
+ if scalar(@cust_svc) >= $quantity && (!$ignore_quantity || !$quantity);
}
''; #no error
diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm
index 9a1b9d864..ff519969d 100644
--- a/FS/FS/part_export.pm
+++ b/FS/FS/part_export.pm
@@ -548,7 +548,7 @@ tie my %shellcommands_options, 'Tie::IxHash',
#'machine' => { label=>'Remote machine' },
'user' => { label=>'Remote username', default=>'root' },
'useradd' => { label=>'Insert command',
- default=>'useradd -d $dir -m -s $shell -u $uid -p $crypt_password $username'
+ default=>'useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username'
#default=>'cp -pr /etc/skel $dir; chown -R $uid.$gid $dir'
},
'useradd_stdin' => { label=>'Insert command STDIN',
@@ -564,7 +564,7 @@ tie my %shellcommands_options, 'Tie::IxHash',
default=>'',
},
'usermod' => { label=>'Modify command',
- default=>'usermod -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username',
+ default=>'usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username',
#default=>'[ -d $old_dir ] && mv $old_dir $new_dir || ( '.
# 'chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; '.
# 'find . -depth -print | cpio -pdm $new_dir; '.
@@ -576,6 +576,9 @@ tie my %shellcommands_options, 'Tie::IxHash',
type =>'textarea',
default=>'',
},
+ 'usermod_pwonly' => { label=>'Disallow username changes',
+ type =>'checkbox',
+ },
'suspend' => { label=>'Suspension command',
default=>'',
},
@@ -613,6 +616,9 @@ tie my %shellcommands_withdomain_options, 'Tie::IxHash',
type =>'textarea',
#default=>"$_password\n$_password\n",
},
+ 'usermod_pwonly' => { label=>'Disallow username changes',
+ type =>'checkbox',
+ },
'suspend' => { label=>'Suspension command',
default=>'',
},
@@ -685,6 +691,12 @@ tie my %sqlradius_options, 'Tie::IxHash',
'password' => { label=>'Database password' },
;
+tie my %sqlradius_withdomain_options, 'Tie::IxHash',
+ 'datasrc' => { label=>'DBI data source ' },
+ 'username' => { label=>'Database username' },
+ 'password' => { label=>'Database password' },
+;
+
tie my %cyrus_options, 'Tie::IxHash',
'server' => { label=>'IMAP server' },
'username' => { label=>'Admin username' },
@@ -692,7 +704,6 @@ tie my %cyrus_options, 'Tie::IxHash',
;
tie my %cp_options, 'Tie::IxHash',
- 'host' => { label=>'Hostname' },
'port' => { label=>'Port number' },
'username' => { label=>'Username' },
'password' => { label=>'Password' },
@@ -876,9 +887,9 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
},
'shellcommands_withdomain' => {
- 'desc' => 'Real-time export via remote SSH.',
+ 'desc' => 'Real-time export via remote SSH (vpopmail, etc.).',
'options' => \%shellcommands_withdomain_options,
- 'notes' => 'Run remote commands via SSH. username@domain (rather than just usernames) are considered unique (also see shellcommands). You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains. You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
+ 'notes' => 'Run remote commands via SSH. username@domain (rather than just usernames) are considered unique (also see shellcommands). You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains. You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="vpopmail" onClick=\'this.form.useradd.value = "/home/vpopmail/bin/vadduser $username\\\@$domain $quoted_password"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "/home/vpopmail/bin/vdeluser $username\\\@$domain"; this.form.userdel_stdin.value=""; this.form.usermod.value = "/home/vpopmail/bin/vpasswd $new_username\\\@$new_domain $new_quoted_password"; this.form.usermod_stdin.value = ""; this.form.usermod_pwonly.checked = true;\'></UL>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
},
'ldap' => {
@@ -891,7 +902,14 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)',
'options' => \%sqlradius_options,
'nodomain' => 'Y',
- 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI-1.23/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source. If using <a href="http://www.freeradius.org/">FreeRADIUS</a> 0.5 or above, make sure your <b>op</b> fields are set to allow NULL values.',
+ 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. This export does not export RADIUS realms (see also sqlradius_withdomain). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.',
+ },
+
+ 'sqlradius_withdomain' => {
+ 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS) with realms',
+ 'options' => \%sqlradius_withdomain_options,
+ 'nodomain' => '',
+ 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. This export exports domains to RADIUS realms (see also sqlradius). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.',
},
'sqlmail' => {
diff --git a/FS/FS/part_export/cp.pm b/FS/FS/part_export/cp.pm
index d998c1d95..c4750dd5d 100644
--- a/FS/FS/part_export/cp.pm
+++ b/FS/FS/part_export/cp.pm
@@ -10,10 +10,10 @@ sub rebless { shift; }
sub _export_insert {
my( $self, $svc_acct ) = (shift, shift);
$self->cp_queue( $svc_acct->svcnum, 'create_mailbox',
- Mailbox => $svc_acct->username,
- Password => $svc_acct->_password,
- Workgroup => $self->option('workgroup'),
- Domain => $svc_acct->domain,
+ 'Mailbox' => $svc_acct->username,
+ 'Password' => $svc_acct->_password,
+ 'Workgroup' => $self->option('workgroup'),
+ 'Domain' => $svc_acct->domain,
);
}
@@ -30,8 +30,30 @@ sub _export_replace {
sub _export_delete {
my( $self, $svc_acct ) = (shift, shift);
$self->cp_queue( $svc_acct->svcnum, 'delete_mailbox',
- Mailbox => $svc_acct->username,
- Domain => $svc_acct->domain,
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status',
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ 'OTHER' => 'T',
+ 'OTHER_SUSPEND' => 'T',
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status',
+ 'Mailbox' => $svc_acct->username,
+ 'Domain' => $svc_acct->domain,
+ 'PAYMENT' => 'F',
+ 'OTHER' => 'F',
+ 'OTHER_SUSPEND' => 'F',
+ 'OTHER_BOUNCE' => 'F',
);
}
@@ -42,7 +64,7 @@ sub cp_queue {
'job' => 'FS::part_export::cp::cp_command',
};
$queue->insert(
- $self->option('host'),
+ $self->machine,
$self->option('port'),
$self->option('username'),
$self->option('password'),
@@ -69,20 +91,22 @@ sub cp_command { #subroutine, not method
);
}
- my $other = 'F';
+ #my $other = 'F';
if ( $new_password =~ /^\*SUSPENDED\* (.*)$/ ) {
$new_password = $1;
- $other = 'T';
+ # $other = 'T';
}
- cp_command($host, $port, $username, $password, 'set_mailbox_status',
- Domain => $domain,
- Mailbox => $new_username,
- Other => $other,
- Other_Bounce => $other,
- );
+ #cp_command($host, $port, $username, $password, $login_domain,
+ # 'set_mailbox_status',
+ # Domain => $domain,
+ # Mailbox => $new_username,
+ # Other => $other,
+ # Other_Bounce => $other,
+ #);
if ( $old_password ne $new_password ) {
- cp_command($host, $port, $username, $password, 'change_mailbox',
+ cp_command($host, $port, $username, $password, $login_domain,
+ 'change_mailbox',
Domain => $domain,
Mailbox => $new_username,
Password => $new_password,
diff --git a/FS/FS/part_export/forward_shellcommands.pm b/FS/FS/part_export/forward_shellcommands.pm
index 43d558a69..f6fcb6062 100644
--- a/FS/FS/part_export/forward_shellcommands.pm
+++ b/FS/FS/part_export/forward_shellcommands.pm
@@ -29,13 +29,13 @@ sub _export_command {
${$_} = $svc_forward->getfield($_) foreach $svc_forward->fields;
}
- my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } );
+ my $svc_acct = $svc_forward->srcsvc_acct;
$username = $svc_acct->username;
$domain = $svc_acct->domain;
- if ($self->dstsvc) {
- $destination = $self->dstsvc_acct->email;
+ if ($svc_forward->dstsvc_acct) {
+ $destination = $svc_forward->dstsvc_acct->email;
} else {
- $destination = $self->dst;
+ $destination = $svc_forward->dst;
}
#done setting variables for the command
@@ -59,22 +59,22 @@ sub _export_replace {
${"new_$_"} = $new->getfield($_) foreach $new->fields;
}
- my $old_svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } );
+ my $old_svc_acct = $old->srcsvc_acct;
$old_username = $old_svc_acct->username;
$old_domain = $old_svc_acct->domain;
- if ($self->dstsvc) {
- $old_destination = $self->dstsvc_acct->email;
+ if ($old->dstsvc_acct) {
+ $old_destination = $old->dstsvc_acct->email;
} else {
- $old_destination = $self->dst;
+ $old_destination = $old->dst;
}
- my $new_svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } );
+ my $new_svc_acct = $new->srcsvc_acct;
$new_username = $new_svc_acct->username;
$new_domain = $new_svc_acct->domain;
- if ($self->dstsvc) {
- $new_destination = $self->dstsvc_acct->email;
+ if ($new->dstsvc) {
+ $new_destination = $new->dstsvc_acct->email;
} else {
- $new_destination = $self->dst;
+ $new_destination = $new->dst;
}
#done setting variables for the command
diff --git a/FS/FS/part_export/shellcommands.pm b/FS/FS/part_export/shellcommands.pm
index f592a838a..edc944009 100644
--- a/FS/FS/part_export/shellcommands.pm
+++ b/FS/FS/part_export/shellcommands.pm
@@ -75,12 +75,24 @@ sub _export_replace {
${"new_$_"} = $new->getfield($_) foreach $new->fields;
}
$new_finger = shell_quote $new_finger;
- $quoted_new__password = shell_quote $new__password;
+ $quoted_new__password = shell_quote $new__password; #old, wrong?
+ $new_quoted_password = shell_quote $new__password; #new, better?
$old_domain = $old->domain;
$new_domain = $new->domain;
$new_crypt_password = ''; #surpress "used only once" warnings
$new_crypt_password = crypt( $new->_password,
$saltset[int(rand(64))].$saltset[int(rand(64))]);
+ if ( $self->option('usermod_pwonly') ) {
+ my $error = '';
+ if ( $old_username ne $new_username ) {
+ $error ||= "can't change username";
+ }
+ if ( $old_domain ne $new_domain ) {
+ $error ||= "can't change domain";
+ }
+ return $error. ' ('. $self->exporttype. ' to '. $self->machine. ')'
+ if $error;
+ }
$self->shellcommands_queue( $new->svcnum,
user => $self->option('user')||'root',
host => $self->machine,
diff --git a/FS/FS/part_export/sqlmail.pm b/FS/FS/part_export/sqlmail.pm
index 64f72df07..8ccad3c7e 100644
--- a/FS/FS/part_export/sqlmail.pm
+++ b/FS/FS/part_export/sqlmail.pm
@@ -1,9 +1,10 @@
package FS::part_export::sqlmail;
use vars qw(@ISA);
+use Digest::MD5 qw(md5_hex);
use FS::Record qw(qsearchs);
use FS::part_export;
-use Digest::MD5 qw(md5_hex);
+use FS::svc_domain;
@ISA = qw(FS::part_export);
diff --git a/FS/FS/part_export/sqlradius.pm b/FS/FS/part_export/sqlradius.pm
index 60450ee63..8a8f9beba 100644
--- a/FS/FS/part_export/sqlradius.pm
+++ b/FS/FS/part_export/sqlradius.pm
@@ -8,6 +8,11 @@ use FS::part_export;
sub rebless { shift; }
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->username;
+}
+
sub _export_insert {
my($self, $svc_acct) = (shift, shift);
@@ -16,14 +21,14 @@ sub _export_insert {
my %attrib = $svc_acct->$method();
next unless keys %attrib;
my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'insert',
- $table, $svc_acct->username, %attrib );
+ $table, $self->export_username($svc_acct), %attrib );
return $err_or_queue unless ref($err_or_queue);
}
my @groups = $svc_acct->radius_groups;
if ( @groups ) {
my $err_or_queue = $self->sqlradius_queue(
$svc_acct->svcnum, 'usergroup_insert',
- $svc_acct->username, @groups );
+ $self->export_username($svc_acct), @groups );
return $err_or_queue unless ref($err_or_queue);
}
'';
@@ -44,9 +49,9 @@ sub _export_replace {
my $dbh = dbh;
my $jobnum = '';
- if ( $old->username ne $new->username ) {
+ if ( $self->export_username($old) ne $self->export_username($new) ) {
my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'rename',
- $new->username, $old->username );
+ $self->export_username($new), $self->export_username($old) );
unless ( ref($err_or_queue) ) {
$dbh->rollback if $oldAutoCommit;
return $err_or_queue;
@@ -63,7 +68,7 @@ sub _export_replace {
} keys %new
) {
my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'insert',
- $table, $new->username, %new );
+ $table, $self->export_username($new), %new );
unless ( ref($err_or_queue) ) {
$dbh->rollback if $oldAutoCommit;
return $err_or_queue;
@@ -80,7 +85,7 @@ sub _export_replace {
my @del = grep { !exists $new{$_} } keys %old;
if ( @del ) {
my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'attrib_delete',
- $table, $new->username, @del );
+ $table, $self->export_username($new), @del );
unless ( ref($err_or_queue) ) {
$dbh->rollback if $oldAutoCommit;
return $err_or_queue;
@@ -109,7 +114,7 @@ sub _export_replace {
if ( @delgroups ) {
my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'usergroup_delete',
- $new->username, @delgroups );
+ $self->export_username($new), @delgroups );
unless ( ref($err_or_queue) ) {
$dbh->rollback if $oldAutoCommit;
return $err_or_queue;
@@ -125,7 +130,7 @@ sub _export_replace {
if ( @newgroups ) {
my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'usergroup_insert',
- $new->username, @newgroups );
+ $self->export_username($new), @newgroups );
unless ( ref($err_or_queue) ) {
$dbh->rollback if $oldAutoCommit;
return $err_or_queue;
@@ -147,7 +152,7 @@ sub _export_replace {
sub _export_delete {
my( $self, $svc_acct ) = (shift, shift);
my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'delete',
- $svc_acct->username );
+ $self->export_username($svc_acct) );
ref($err_or_queue) ? '' : $err_or_queue;
}
diff --git a/FS/FS/part_export/sqlradius_withdomain.pm b/FS/FS/part_export/sqlradius_withdomain.pm
new file mode 100644
index 000000000..1c8f38c9d
--- /dev/null
+++ b/FS/FS/part_export/sqlradius_withdomain.pm
@@ -0,0 +1,12 @@
+package FS::part_export::sqlradius_withdomain;
+
+use vars qw(@ISA);
+use FS::part_export::sqlradius;
+
+@ISA = qw(FS::part_export::sqlradius);
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->email;
+}
+
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index 60b0e01f9..6525864c4 100644
--- a/FS/FS/part_pkg.pm
+++ b/FS/FS/part_pkg.pm
@@ -267,11 +267,12 @@ SVCDB is specified and does not match the svcdb of the service definition,
sub svcpart {
my $self = shift;
- my $svcdb = shift;
- my @pkg_svc = $self->pkg_svc;
- return '' if scalar(@pkg_svc) != 1
- || $pkg_svc[0]->quantity != 1
- || ( $svcdb && $pkg_svc[0]->part_svc->svcdb ne $svcdb );
+ my $svcdb = scalar(@_) ? shift : '';
+ my @pkg_svc = grep {
+ $_->quantity == 1
+ && ( $svcdb eq $_->part_svc->svcdb || !$svcdb )
+ } $self->pkg_svc;
+ return '' if scalar(@pkg_svc) != 1;
$pkg_svc[0]->svcpart;
}
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
index 497e59c94..5b8107fc8 100644
--- a/FS/FS/svc_acct.pm
+++ b/FS/FS/svc_acct.pm
@@ -9,7 +9,7 @@ use vars qw( @ISA $DEBUG $me $conf
$username_uppercase
$welcome_template $welcome_from $welcome_subject $welcome_mimetype
$smtpmachine
- $radius_password
+ $radius_password $radius_ip
$dirhash
@saltset @pw_set );
use Carp;
@@ -68,6 +68,7 @@ $FS::UID::callback{'FS::svc_acct'} = sub {
}
$smtpmachine = $conf->config('smtpmachine');
$radius_password = $conf->config('radius-password') || 'Password';
+ $radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address';
};
@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
@@ -603,6 +604,8 @@ error, returns the error, otherwise returns false.
Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+Calls any export-specific suspend hooks.
+
=cut
sub suspend {
@@ -627,6 +630,8 @@ an error, returns the error, otherwise returns false.
Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+Calls any export-specific unsuspend hooks.
+
=cut
sub unsuspend {
@@ -783,12 +788,14 @@ sub check {
$recref->{quota} = $1;
unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) {
- unless ( $recref->{slipip} eq '0e0' ) {
+ if ( $recref->{slipip} eq '' ) {
+ $recref->{slipip} = '';
+ } elsif ( $recref->{slipip} eq '0e0' ) {
+ $recref->{slipip} = '0e0';
+ } else {
$recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
or return "Illegal slipip: ". $self->slipip;
$recref->{slipip} = $1;
- } else {
- $recref->{slipip} = '0e0';
}
}
@@ -860,7 +867,7 @@ sub radius_reply {
( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
} grep { /^radius_/ && $self->getfield($_) } fields( $self->table );
if ( $self->slipip && $self->slipip ne '0e0' ) {
- $reply{'Framed-IP-Address'} = $self->slipip;
+ $reply{$radius_ip} = $self->slipip;
}
%reply;
}
diff --git a/FS/FS/svc_acct_pop.pm b/FS/FS/svc_acct_pop.pm
index 3c9ea0130..196ab7ebb 100644
--- a/FS/FS/svc_acct_pop.pm
+++ b/FS/FS/svc_acct_pop.pm
@@ -142,8 +142,7 @@ sub popselector {
function popstate_changed(what) {
state = what.options[what.selectedIndex].text;
- for (var i = what.form.popnum.length;i > 0;i--)
- what.form.popnum.options[i] = null;
+ what.form.popnum.options.length = 0
what.form.popnum.options[0] = new Option("", "", false, true);
END
@@ -167,7 +166,13 @@ END
$text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD><TD>';
$text .= qq!<SELECT NAME="popnum" SIZE=1><OPTION> !;
- foreach my $pop ( @svc_acct_pop ) {
+ my @initial_select;
+ if ( scalar(@svc_acct_pop) > 100 ) {
+ @initial_select = qsearchs( 'svc_acct_pop', { 'popnum' => $popnum } );
+ } else {
+ @initial_select = @svc_acct_pop;
+ }
+ foreach my $pop ( @initial_select ) {
$text .= qq!<OPTION VALUE="!. $pop->popnum. '"'.
( ( $popnum && $pop->popnum == $popnum ) ? ' SELECTED' : '' ). ">".
$pop->text;
@@ -182,7 +187,7 @@ END
=head1 VERSION
-$Id: svc_acct_pop.pm,v 1.7 2002-04-10 13:42:48 ivan Exp $
+$Id: svc_acct_pop.pm,v 1.9 2003-07-04 01:37:46 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
index 2e8866a56..32b94563d 100644
--- a/FS/FS/svc_domain.pm
+++ b/FS/FS/svc_domain.pm
@@ -324,7 +324,7 @@ sub check {
}
#if ( $recref->{domain} =~ /^([\w\-\.]{1,22})\.(com|net|org|edu)$/ ) {
- if ( $recref->{domain} =~ /^([\w\-]{1,22})\.(com|net|org|edu)$/ ) {
+ if ( $recref->{domain} =~ /^([\w\-]{1,63})\.(com|net|org|edu)$/ ) {
$recref->{domain} = "$1.$2";
# hmmmmmmmm.
} elsif ( $whois_hack && $recref->{domain} =~ /^([\w\-\.]+)$/ ) {
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 272b5b731..846f37310 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -3,31 +3,31 @@ MANIFEST
MANIFEST.SKIP
Makefile.PL
README
-bin/freeside-bill
-bin/freeside-daily
-bin/freeside-email
-bin/freeside-queued
bin/freeside-addoutsource
bin/freeside-addoutsourceuser
+bin/freeside-adduser
+bin/freeside-apply-credits
+bin/freeside-bill
+bin/freeside-cc-receipts-report
+bin/freeside-count-active-customers
+bin/freeside-credit-report
+bin/freeside-daily
bin/freeside-deloutsource
bin/freeside-deloutsourceuser
-bin/freeside-apply-credits
-bin/freeside-adduser
bin/freeside-deluser
-bin/freeside-setup
-bin/freeside-setinvoice
-bin/freeside-overdue
+bin/freeside-email
+bin/freeside-expiration-alerter
+bin/freeside-queued
bin/freeside-radgroup
bin/freeside-receivables-report
+bin/freeside-reexport
bin/freeside-selfservice-server
+bin/freeside-setinvoice
+bin/freeside-setup
bin/freeside-sqlradius-radacctd
bin/freeside-sqlradius-reset
bin/freeside-sqlradius-seconds
bin/freeside-tax-report
-bin/freeside-cc-receipts-report
-bin/freeside-credit-report
-bin/freeside-expiration-alerter
-bin/freeside-reexport
FS.pm
FS/CGI.pm
FS/InitHandler.pm
@@ -130,6 +130,7 @@ t/Misc.t
t/Record.t
t/UID.t
t/Msgcat.t
+t/SearchCache.t
t/cust_bill.t
t/cust_bill_event.t
t/cust_bill_pay.t
@@ -146,6 +147,7 @@ t/cust_pay_batch.t
t/cust_pkg.t
t/cust_refund.t
t/cust_svc.t
+t/cust_tax_exempt.t
t/domain_record.t
t/nas.t
t/part_bill_event.t
@@ -190,6 +192,6 @@ t/svc_www.t
t/type_pkgs.t
t/queue.t
t/queue_arg.t
+t/queue_depend.t
t/msgcat.t
t/raddb.t
-t/cust_tax_exempt.t
diff --git a/FS/bin/freeside-overdue b/FS/bin/freeside-overdue
deleted file mode 100755
index 116245f9c..000000000
--- a/FS/bin/freeside-overdue
+++ /dev/null
@@ -1,196 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use vars qw( $days_to_pay $cust_main $cust_pkg
- $cust_svc $svc_acct );
-use Getopt::Std;
-use FS::cust_main;
-use FS::cust_pkg;
-use FS::cust_svc;
-use FS::svc_acct;
-use FS::Record qw(qsearch qsearchs);
-use FS::UID qw(adminsuidsetup);
-
-&untaint_argv;
-my %opt;
-getopts('ed:qpl:scbyoi', \%opt);
-my $user = shift or die &usage;
-
-adminsuidsetup $user;
-
-my $now = time; #eventually take a time option like freeside-bill
-my ($sec,$min,$hour,$mday,$mon,$year) =
- (localtime($now) )[0,1,2,3,4,5];
-$mon++;
-$year += 1900;
-
-foreach $cust_main ( qsearch('cust_main',{} ) ) {
-
- my ( $eyear, $emon, $eday ) = ( 2037, 12, 31 );
- if ( $cust_main->paydate =~ /^(\d{4})\-(\d{1,2})\-(\d{1,2})$/
- && $cust_main->payby eq 'BILL') {
- ( $eyear, $emon, $eday ) = ( $1, $2, $3 );
- }
-
- if ( ( $opt{d}
- && $cust_main->balance_date(time - $opt{d} * 86400) > 0
- && qsearchs( 'cust_pkg', { 'custnum' => $cust_main->custnum,
- 'susp' => "" } ) )
- || ( $opt{e}
- && $cust_main->payby eq 'BILL'
- && ( $eyear < $year
- || ( $eyear == $year && $emon < $mon ) ) )
- ) {
-
- unless ( $opt{q} ) {
- print $cust_main->custnum, "\t",
- $cust_main->last, "\t", $cust_main->first, "\t",
- $cust_main->balance_date(time-$opt{d} * 86400);
- }
-
- if ( $opt{p} && ! grep { $_ eq 'POST' } $cust_main->invoicing_list ) {
- print "\n\tAdding postal invoicing" unless $opt{q};
- my @invoicing_list = $cust_main->invoicing_list;
- push @invoicing_list, 'POST';
- $cust_main->invoicing_list(\@invoicing_list);
- }
-
- if ( $opt{l} ) {
- print "\n\tCharging late fee of \$$opt{l}" unless $opt{q};
- my $error = $cust_main->charge($opt{l}, 'Late fee');
- # comment or plandata with info so we don't redo the same late fee every
- # day
- }
-
- foreach $cust_pkg ( qsearch( 'cust_pkg',
- { 'custnum' => $cust_main->custnum } ) ) {
-
- if ($opt{s}) {
- print "\n\tSuspending pkgnum " . $cust_pkg->pkgnum unless $opt{q};
- $cust_pkg->suspend;
- }
-
- if ($opt{c}) {
- print "\n\tCancelling pkgnum " . $cust_pkg->pkgnum unless $opt{q};
- $cust_pkg->cancel;
- }
-
- }
-
- if ( $opt{b} ) {
- print "\n\tBilling" unless $opt{q};
- my $error = $cust_main->bill('time'=>$now);
- warn "Error billing, customer #" . $cust_main->custnum .
- ":" . $error if $error;
- }
-
- if ( $opt{y} ) {
- print "\n\tApplying outstanding payments and credits" unless $opt{q};
- $cust_main->apply_payments;
- $cust_main->apply_credits;
- }
-
- if ( $opt{o} ) {
- print "\n\tCollecting" unless $opt{q};
- my $error = $cust_main->collect(
- 'invoice_time' => $now,
- 'batch_card' => $opt{i} ? 'no' : 'yes',
- 'force_print' => 'yes',
- );
- warn "Error collecting from customer #" . $cust_main->custnum. ":$error"
- if $error;
- }
-
- print "\n" unless $opt{q};
-
- }
-
-}
-
-sub untaint_argv {
- foreach $_ ( $[ .. $#ARGV ) {
- $ARGV[$_] =~ /^([\w\-\/\.]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
- $ARGV[$_]=$1;
- }
-}
-
-sub usage {
- die "Usage:\n\n freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user\n";
-}
-
-
-=head1 NAME
-
-freeside-overdue - Perform actions on overdue and/or expired accounts.
-
-=head1 SYNOPSIS
-
- freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user
-
-=head1 DESCRIPTION
-
-This script is deprecated in 1.4.0. You should use freeside-daily and invoice
-events instead.
-
-Performs actions on overdue and/or expired accounts.
-
-Selection options (at least one selection option is required):
-
- -d: Customers with a balance due on invoices older than the supplied number
- of days. Requires an integer argument.
-
- -e: Customers with a billing expiration date in the past.
-
-Action options:
-
- -q: Be quiet (by default, selected accounts are printed).
-
- -p: Add postal invoicing to the relevant customers.
-
- -l: Add a charge of the given amount to the relevant customers.
-
- -s: Suspend accounts.
-
- -c: Cancel accounts.
-
- -b: Bill customers (create invoices)
-
- -y: Apply unapplied payments and credits
-
- -o: Collect from customers (charge cards, print invoices)
-
- -i: real-time billing (as opposed to batch billing). only relevant
- for credit cards.
-
- user: From the mapsecrets file - see config.html from the base documentation
-
-=head1 CRONTAB
-
-Example crontab entries:
-
-# suspend expired accounts
-20 4 * * * freeside-overdue -e -s user
-
-# quietly add postal invoicing to customers over 30 days past due
-20 4 * * * freeside-overdue -d 30 -p -q user
-
-# suspend accounts and charge a $10.23 fee for customers over 60 days past due
-20 4 * * * freeside-overdue -d 60 -s -l 10.23 user
-
-# cancel accounts over 90 days past due
-20 4 * * * freeside-overdue -d 90 -c user
-
-=head1 ORIGINAL AUTHORS
-
-Original disable-overdue version by mw/kwh: Mark W.? and Kristian Hoffmann ?
-
-Ivan seems to be turning it into the "do-everything" CLI.
-
-=head1 BUGS
-
-Hell now that this is the do-everything CLI it should have --longoptions
-
-=cut
-
-1;
-
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
index 518a2ad42..734744efe 100755
--- a/FS/bin/freeside-setup
+++ b/FS/bin/freeside-setup
@@ -373,7 +373,7 @@ sub tables_hash_hack {
],
'primary_key' => 'invnum',
'unique' => [],
- 'index' => [ ['custnum'] ],
+ 'index' => [ ['custnum'], ['_date'] ],
},
'cust_bill_event' => {
@@ -1074,7 +1074,7 @@ sub tables_hash_hack {
'addr_block' => {
'columns' => [
- 'blocknum', 'int', '', '',
+ 'blocknum', 'serial', '', '',
'routernum', 'int', '', '',
'ip_gateway', 'varchar', '', 15,
'ip_netmask', 'int', '', '',
@@ -1086,7 +1086,7 @@ sub tables_hash_hack {
'part_sb_field' => {
'columns' => [
- 'sbfieldpart', 'int', '', '',
+ 'sbfieldpart', 'serial', '', '',
'svcpart', 'int', '', '',
'name', 'varchar', '', $char_d,
'length', 'int', '', '',
diff --git a/FS/bin/freeside-sqlradius-reset b/FS/bin/freeside-sqlradius-reset
index 9d3a6a700..74f90a582 100755
--- a/FS/bin/freeside-sqlradius-reset
+++ b/FS/bin/freeside-sqlradius-reset
@@ -12,7 +12,9 @@ adminsuidsetup $user;
#my $machine = shift or die &usage;
-my @exports = qsearch('part_export', { 'exporttype' => 'sqlradius' } );
+my @exports = qsearch('part_export', { exporttype=>'sqlradius' } );
+push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } );
+
foreach my $export ( @exports ) {
my $icradius_dbh = DBI->connect(
diff --git a/FS/t/part_export-sqlradius_withdomain.t b/FS/t/part_export-sqlradius_withdomain.t
new file mode 100644
index 000000000..504bf679f
--- /dev/null
+++ b/FS/t/part_export-sqlradius_withdomain.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_export::sqlradius_withdomain;
+$loaded=1;
+print "ok 1\n";