summaryrefslogtreecommitdiff
path: root/FS/bin
diff options
context:
space:
mode:
Diffstat (limited to 'FS/bin')
-rw-r--r--FS/bin/freeside-adduser56
-rwxr-xr-xFS/bin/freeside-cdr-sftp_and_import63
-rw-r--r--FS/bin/freeside-cdrrated16
-rw-r--r--FS/bin/freeside-cdrrewrited60
-rw-r--r--FS/bin/freeside-censustract-update52
-rwxr-xr-xFS/bin/freeside-daily13
-rw-r--r--FS/bin/freeside-deluser64
-rwxr-xr-xFS/bin/freeside-eftca-download11
-rwxr-xr-xFS/bin/freeside-email2
-rwxr-xr-xFS/bin/freeside-fetch2
-rw-r--r--FS/bin/freeside-ipifony-download320
-rwxr-xr-xFS/bin/freeside-monthly6
-rwxr-xr-xFS/bin/freeside-phonenum_list86
-rw-r--r--FS/bin/freeside-queued14
-rw-r--r--FS/bin/freeside-selfservice-server29
-rwxr-xr-xFS/bin/freeside-selfservice-xmlrpcd4
-rwxr-xr-xFS/bin/freeside-setup2
-rwxr-xr-xFS/bin/freeside-upgrade4
-rwxr-xr-xFS/bin/freeside-username_list84
-rwxr-xr-xFS/bin/freeside-void-payments13
-rwxr-xr-xFS/bin/freeside-wkhtmltopdf8
21 files changed, 674 insertions, 235 deletions
diff --git a/FS/bin/freeside-adduser b/FS/bin/freeside-adduser
index 530481377..6bfb759f8 100644
--- a/FS/bin/freeside-adduser
+++ b/FS/bin/freeside-adduser
@@ -7,46 +7,9 @@ use Getopt::Std;
my $FREESIDE_CONF = "%%%FREESIDE_CONF%%%";
-getopts("s:g:n");
+getopts("g:");
my $user = shift or die &usage;
-if ( $opt_s ) {
-
- #if ( -e "$FREESIDE_CONF/mapsecrets" ) {
- # open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
- # or die "can't open $FREESIDE_CONF/mapsecrets: $!";
- # while (<MAPSECRETS>) {
- # /^(\S+) / or die "unparsable line in mapsecrets: $_";
- # die "user $user already exists\n" if $user eq $1;
- # }
- # close MAPSECRETS;
- #}
-
- #insert new entry before a wildcard...
- open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
- and flock(MAPSECRETS,LOCK_EX)
- or die "can't open $FREESIDE_CONF/mapsecrets: $!";
- open(NEW,">$FREESIDE_CONF/mapsecrets.new")
- or die "can't open $FREESIDE_CONF/mapsecrets.new: $!";
- while(<MAPSECRETS>) {
- if ( /^\*\s/ ) {
- print NEW "$user $opt_s\n";
- }
- print NEW $_;
- }
- close MAPSECRETS or die "can't close $FREESIDE_CONF/mapsecrets: $!";
- close NEW or die "can't close $FREESIDE_CONF/mapsecrets.new: $!";
- rename("$FREESIDE_CONF/mapsecrets.new", "$FREESIDE_CONF/mapsecrets")
- or die "can't move mapsecrets.new into place: $!";
-
-}
-
-###
-
-exit if $opt_n;
-
-###
-
use FS::UID qw(adminsuidsetup);
use FS::CurrentUser;
use FS::access_user;
@@ -58,7 +21,7 @@ adminsuidsetup $user;
my $access_user = new FS::access_user {
'username' => $user,
- '_password' => 'notyet',
+ '_password' => '',
'first' => 'Firstname', # $opt_f ||
'last' => 'Lastname', # $opt_l ||
};
@@ -79,7 +42,7 @@ if ( $opt_g ) {
###
sub usage {
- die "Usage:\n\n freeside-adduser [ -n ] [ -s ] [ -g groupnum ] username [ password ]"
+ die "Usage:\n\n freeside-adduser [ -g groupnum ] username [ password ]"
}
=head1 NAME
@@ -88,7 +51,7 @@ freeside-adduser - Command line interface to add (freeside) users.
=head1 SYNOPSIS
- freeside-adduser [ -n ] [ -s ] [ -g groupnum ] username [ password ]
+ freeside-adduser [ -g groupnum ] username [ password ]
=head1 DESCRIPTION
@@ -100,17 +63,6 @@ B<Configuration | Employees | View/Edit employees>.
-g: initial groupnum
- Development/multi-DB options:
-
- -s: alternate secrets file
-
- -n: no ACL added, for bootstrapping
-
-=head1 NOTE
-
-No explicit htpasswd options are available in 1.7 - passwords are now
-maintained automatically.
-
=head1 SEE ALSO
Base Freeside documentation
diff --git a/FS/bin/freeside-cdr-sftp_and_import b/FS/bin/freeside-cdr-sftp_and_import
index 7f2693fcb..aa1b3942c 100755
--- a/FS/bin/freeside-cdr-sftp_and_import
+++ b/FS/bin/freeside-cdr-sftp_and_import
@@ -12,8 +12,8 @@ use FS::cdr;
# parse command line
###
-use vars qw( $opt_m $opt_p $opt_r $opt_e $opt_d $opt_v $opt_P $opt_a $opt_c $opt_g );
-getopts('c:m:p:r:e:d:v:P:ag');
+use vars qw( $opt_m $opt_p $opt_r $opt_e $opt_d $opt_v $opt_P $opt_a $opt_c $opt_g $opt_s $opt_b );
+getopts('c:m:p:r:e:d:v:P:agsb');
$opt_e ||= 'csv';
#$opt_e = ".$opt_e" unless $opt_e =~ /^\./;
@@ -116,31 +116,39 @@ foreach my $filename ( @$ls ) {
$import_options->{'cdrtypenum'} = $opt_c if $opt_c;
my $error = FS::cdr::batch_import($import_options);
+
if ( $error ) {
- unlink "$cachedir/$filename";
- unlink "$cachedir/$ungziped" if $opt_g;
- die $error;
- }
- if ( $opt_d ) {
- if($opt_m eq 'ftp') {
- my $ftp = ftp();
- $ftp->rename($filename, "$opt_d/$file_timestamp")
- or do {
- unlink "$cachedir/$filename";
- unlink "$cachedir/$ungziped" if $opt_g;
- die "Can't move $filename to $opt_d: ".$ftp->message . "\n";
- };
+ if ( $opt_s ) {
+ warn "$ungziped: $error\n";
+ } else {
+ unlink "$cachedir/$filename";
+ unlink "$cachedir/$ungziped" if $opt_g;
+ die $error;
}
- else {
- my $sftp = sftp();
- $sftp->rename($filename, "$opt_d/$file_timestamp")
- or do {
- unlink "$cachedir/$filename";
- unlink "$cachedir/$ungziped" if $opt_g;
- die "can't move $filename to $opt_d: ". $sftp->error . "\n";
- };
+
+ } else {
+
+ if ( $opt_d ) {
+ if ( $opt_m eq 'ftp') {
+ my $ftp = ftp();
+ $ftp->rename($filename, "$opt_d/$file_timestamp")
+ or do {
+ unlink "$cachedir/$filename";
+ unlink "$cachedir/$ungziped" if $opt_g;
+ die "Can't move $filename to $opt_d: ".$ftp->message . "\n";
+ };
+ } else {
+ my $sftp = sftp();
+ $sftp->rename($filename, "$opt_d/$file_timestamp")
+ or do {
+ unlink "$cachedir/$filename";
+ unlink "$cachedir/$ungziped" if $opt_g;
+ die "can't move $filename to $opt_d: ". $sftp->error . "\n";
+ };
+ }
}
+
}
unlink "$cachedir/$filename";
@@ -168,6 +176,7 @@ sub ftp {
or die "FTP connection to '$hostname' failed.";
$ftp->login($ftp_user, $ftp_pass) or die "FTP login failed: ".$ftp->message;
$ftp->cwd($opt_r) or die "can't chdir to $opt_r\n" if $opt_r;
+ $ftp->binary or die "can't set BINARY mode: ". $ftp->message if $opt_b;
return $ftp;
}
@@ -192,7 +201,7 @@ freeside-cdr-sftp_and_import - Download CDR files from a remote server via SFTP
cdr.sftp_and_import [ -m method ] [ -p prefix ] [ -e extension ]
[ -r remotefolder ] [ -d donefolder ] [ -v level ] [ -P port ]
- [ -a ] [ -c cdrtypenum ] user format [sftpuser@]servername
+ [ -a ] [ -g ] [ -s ] [ -c cdrtypenum ] user format [sftpuser@]servername
=head1 DESCRIPTION
@@ -213,11 +222,17 @@ or FTP and then import them into the database.
-a: use ftp passive mode
+-b: use ftp binary mode
+
-v: set verbosity level; this script only has one level, but it will
be passed as the 'debug' argument to the transport method
-c: cdrtypenum to set, defaults to none
+-g: File is gzipped
+
+-s: Warn and skip files which could not be imported rather than abort
+
user: freeside username
format: CDR format name
diff --git a/FS/bin/freeside-cdrrated b/FS/bin/freeside-cdrrated
index 131b56a7e..99ea67594 100644
--- a/FS/bin/freeside-cdrrated
+++ b/FS/bin/freeside-cdrrated
@@ -33,9 +33,11 @@ if ( @cdrtypenums ) {
$extra_sql .= ' AND cdrtypenum IN ('. join(',', @cdrtypenums ). ')';
}
-our %svcnum = ();
-our %pkgpart = ();
-our %part_pkg = ();
+our %svcnum = (); # phonenum => svcnum
+our %pkgnum = (); # phonenum => pkgnum
+our %cust_pkg = (); # pkgnum => cust_pkg (NOT phonenum => cust_pkg!)
+our %pkgpart = (); # phonenum => pkgpart
+our %part_pkg = (); # phonenum => part_pkg
#some false laziness w/freeside-cdrrewrited
@@ -91,6 +93,9 @@ while (1) {
next;
}
+ $pkgnum{$number} = $cust_pkg->pkgnum;
+ $cust_pkg{$cust_pkg->pkgnum} ||= $cust_pkg;
+
#get the package, search through the part_pkg and linked for a voip_cdr def w/matching cdrtypenum (or no use_cdrtypenum)
my @part_pkg =
grep { $_->plan eq 'voip_cdr'
@@ -126,10 +131,11 @@ while (1) {
#}
#XXX if $part_pkg->option('min_included') then we can't prerate this CDR
-
+
my $error = $cdr->rate(
'part_pkg' => $part_pkg{ $pkgpart{$number} },
- 'svcnum' => $svcnum{ $number },
+ 'cust_pkg' => $cust_pkg{ $pkgnum{$number} },
+ 'svcnum' => $svcnum{$number},
);
if ( $error ) {
#XXX ???
diff --git a/FS/bin/freeside-cdrrewrited b/FS/bin/freeside-cdrrewrited
index f2c3926fb..16f931fbf 100644
--- a/FS/bin/freeside-cdrrewrited
+++ b/FS/bin/freeside-cdrrewrited
@@ -30,9 +30,9 @@ die "not running; cdr-asterisk_forward_rewrite, cdr-charged_party_rewrite ".
#--
-my %accountcode_unmatch = ();
-my $accountcode_retry = 4 * 60 * 60; # 4 hours
-my $accountcode_giveup = 4 * 24 * 60 * 60; # 4 days
+my %sessionnum_unmatch = ();
+my $sessionnum_retry = 4 * 60 * 60; # 4 hours
+my $sessionnum_giveup = 4 * 24 * 60 * 60; # 4 days
my %cdr_type = map { lc($_->cdrtypename) => $_->cdrtypenum }
qsearch('cdr_type',{});
@@ -45,8 +45,8 @@ while (1) {
# instead of just doing this search like normal CDRs
#hmm :/
- my @recent = grep { ($accountcode_unmatch{$_} + $accountcode_retry) > time }
- keys %accountcode_unmatch;
+ my @recent = grep { ($sessionnum_unmatch{$_} + $sessionnum_retry) > time }
+ keys %sessionnum_unmatch;
my $extra_sql = scalar(@recent)
? ' AND acctid NOT IN ('. join(',', @recent). ') '
: '';
@@ -136,45 +136,62 @@ while (1) {
}
- if ( $conf->exists('cdr-taqua-accountcode_rewrite')
- && $cdr->lastapp eq 'acctcode' && $cdr->cdrtypenum == 1
+ if ( $cdr->cdrtypenum == 1
+ and $cdr->lastapp
+ and (
+ $conf->exists('cdr-taqua-accountcode_rewrite') or
+ $conf->exists('cdr-taqua-callerid_rewrite') )
)
{
#find the matching CDR
- my $primary = qsearchs('cdr', {
- 'sessionnum' => $cdr->sessionnum,
- 'src' => $cdr->subscriber,
- #'accountcode' => '',
- });
+ my %search = ( 'sessionnum' => $cdr->sessionnum );
+ if ( $cdr->lastapp eq 'acctcode' ) {
+ $search{'src'} = $cdr->subscriber;
+ } elsif ( $cdr->lastapp eq 'CallerId' ) {
+ $search{'dst'} = $cdr->subscriber;
+ }
+ my $primary = qsearchs('cdr', \%search);
unless ( $primary ) {
my $cantfind = "can't find primary CDR with session ". $cdr->sessionnum.
", src ". $cdr->subscriber;
- if ( $cdr->calldate_unix + $accountcode_giveup < time ) {
+ if ( $cdr->calldate_unix + $sessionnum_giveup < time ) {
warn "ERROR: $cantfind; giving up\n";
- push @status, 'taqua-accountcode-NOTFOUND';
+ push @status, 'taqua-sessionnum-NOTFOUND';
$cdr->status('done'); #so it doesn't try to rate
- delete $accountcode_unmatch{$cdr->acctid}; #so it doesn't suck mem
+ delete $sessionnum_unmatch{$cdr->acctid}; #so it doesn't suck mem
} else {
warn "WARNING: $cantfind; will keep trying\n";
- $accountcode_unmatch{$cdr->acctid} = time;
+ $sessionnum_unmatch{$cdr->acctid} = time;
next;
}
} else {
- $primary->accountcode( $cdr->lastdata );
+ if ( $cdr->lastapp eq 'acctcode' ) {
+ # lastdata contains the dialed account code
+ $primary->accountcode( $cdr->lastdata );
+ push @status, 'taqua-accountcode';
+ } elsif ( $cdr->lastapp eq 'CallerId' ) {
+ # lastdata contains "allowed" or "restricted"
+ # or case variants thereof
+ if ( lc($cdr->lastdata) eq 'restricted' ) {
+ $primary->clid( 'PRIVATE' );
+ }
+ push @status, 'taqua-callerid';
+ } else {
+ warn "unknown Taqua service name: ".$cdr->lastapp."\n";
+ }
#$primary->freesiderewritestatus( 'taqua-accountcode-primary' );
- my $error = $primary->replace;
+ my $error = $primary->replace if $primary->modified;
if ( $error ) {
warn "WARNING: error rewriting primary CDR (will retry): $error\n";
next;
}
$skip{$primary->acctid} = 1;
- push @status, 'taqua-accountcode';
$cdr->status('done'); #so it doesn't try to rate
}
@@ -214,7 +231,10 @@ sub _shouldrun {
$conf->exists('cdr-asterisk_forward_rewrite')
|| $conf->exists('cdr-asterisk_australia_rewrite')
|| $conf->exists('cdr-charged_party_rewrite')
- || $conf->exists('cdr-taqua-accountcode_rewrite');
+ || $conf->exists('cdr-taqua-accountcode_rewrite')
+ || $conf->exists('cdr-taqua-callerid_rewrite')
+ || 0
+ ;
}
sub usage {
diff --git a/FS/bin/freeside-censustract-update b/FS/bin/freeside-censustract-update
index 8c6721b3e..af9ad749b 100644
--- a/FS/bin/freeside-censustract-update
+++ b/FS/bin/freeside-censustract-update
@@ -6,8 +6,8 @@ use Date::Parse 'str2time';
use FS::UID qw(adminsuidsetup);
use FS::Record qw(qsearch dbh);
use FS::Conf;
-use FS::cust_main;
-use FS::h_cust_main;
+use FS::cust_location;
+use FS::h_cust_location;
my %opt;
getopts('d:', \%opt);
@@ -22,40 +22,48 @@ my $current_year = $conf->config('census_year')
or die "No current census year configured.\n";
my $date = str2time($opt{d}) if $opt{d};
$date ||= time;
-my %h_cust_main = map { $_->custnum => $_ }
+# This now operates on cust_location, not cust_main.
+# Find all locations that, as of $date, did not have
+# censusyear = the current year. This includes those
+# that have no censusyear.
+my %h_cust_location = map { $_->locationnum => $_ }
qsearch(
- 'h_cust_main',
+ 'h_cust_location',
{ censusyear => { op => '!=', value => $current_year } },
- FS::h_cust_main->sql_h_search($date),
- ) ; #the state of these customers as of $date
+ FS::h_cust_location->sql_h_search($date),
+ ) ;
-my @cust_main = qsearch( 'cust_main',
+# Find all locations that don't have censusyear = the current
+# year as of now.
+my @cust_location = qsearch( 'cust_location',
{ censusyear => { op => '!=', value => $current_year } },
-); # all possibly interesting customers
+);
-warn scalar(@cust_main)." records found.\n";
+warn scalar(@cust_location)." records found.\n";
my $queued = 0; my $updated = 0;
-foreach my $cust_main (@cust_main) {
+foreach my $cust_location (@cust_location) {
my $error;
- my $h = $h_cust_main{$cust_main->custnum};
- if ( defined($h) and $h->censustract eq $cust_main->censustract ) {
- # the tract code hasn't been changed since $date
- # so update it now
+ my $h = $h_cust_location{$cust_location->locationnum};
+ if ( defined($h) and $h->censustract eq $cust_location->censustract ) {
+ # Then the location's censustract hasn't been changed since $date
+ # (or it didn't exist on $date, or $date is now). Queue a censustract
+ # update for it.
my $job = FS::queue->new({
- job => 'FS::cust_main::process_censustract_update'
+ job => 'FS::cust_location::process_censustract_update'
});
- $error = $job->insert($cust_main->custnum);
+ $error = $job->insert($cust_location->locationnum);
$queued++;
}
- elsif ($cust_main->censusyear eq '') {
- # the tract number is assumed current, so just set the year
- $cust_main->set('censusyear', $current_year);
- $error = $cust_main->replace;
+ elsif ($cust_location->censusyear eq '') {
+ # Then it's been updated since $date, but somehow has a null censusyear.
+ # (Is this still relevant?)
+ $cust_location->set('censusyear', $current_year);
+ $error = $cust_location->replace;
$updated++;
- }
+ } # Else it's been updated since $date, so leave it alone.
if ( $error ) {
$dbh->rollback;
- die "error updating ".$cust_main->custnum.": $error\n";
+ die "error updating ".$cust_location->locationnum.": $error\n";
}
}
warn "Queued $queued census code lookups, updated year in $updated records.\n";
diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily
index 8e8ae4ff9..b6ee5188e 100755
--- a/FS/bin/freeside-daily
+++ b/FS/bin/freeside-daily
@@ -4,6 +4,7 @@ use strict;
use Getopt::Std;
use FS::UID qw(adminsuidsetup);
use FS::Conf;
+use FS::Log;
&untaint_argv; #what it sounds like (eww)
use vars qw(%opt);
@@ -11,6 +12,8 @@ getopts("p:a:d:vl:sy:nmrkg:o", \%opt);
my $user = shift or die &usage;
adminsuidsetup $user;
+my $log = FS::Log->new('daily');
+$log->info('start');
#you can skip this by not having a NetworkMonitoringSystem configured
use FS::Cron::nms_report qw(nms_report);
@@ -74,6 +77,12 @@ unlink <${deldir}.CGItemp*>;
use FS::Cron::backup qw(backup);
backup();
+#except we'd rather not start cleanup jobs until the backup is done
+use FS::Cron::cleanup qw(cleanup);
+cleanup();
+
+$log->info('finish');
+
###
# subroutines
###
@@ -138,13 +147,13 @@ the bill and collect methods of a cust_main object. See L<FS::cust_main>.
-l: debugging level
- -m: Experimental multi-process mode uses the job queue for multi-process and/or multi-machine billing.
+ -m: Multi-process mode uses the job queue for multi-process and/or multi-machine billing.
-r: Multi-process mode dry run option
-k: skip notify_flat_delay
-user: From the mapsecrets file - see config.html from the base documentation
+user: Typically "fs_daily"
custnum: if one or more customer numbers are specified, only bills those
customers. Otherwise, bills all customers.
diff --git a/FS/bin/freeside-deluser b/FS/bin/freeside-deluser
deleted file mode 100644
index a2a361a83..000000000
--- a/FS/bin/freeside-deluser
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use vars qw($opt_h);
-use Fcntl qw(:flock);
-use Getopt::Std;
-
-my $FREESIDE_CONF = "%%%FREESIDE_CONF%%%";
-
-getopts("h:");
-my $user = shift or die &usage;
-
-if ( $opt_h ) {
- open(HTPASSWD,"<$opt_h")
- and flock(HTPASSWD,LOCK_EX)
- or die "can't open $opt_h: $!";
- open(HTPASSWD_TMP,">$opt_h.tmp") or die "can't open $opt_h.tmp: $!";
- while (<HTPASSWD>) {
- print HTPASSWD_TMP $_ unless /^$user:/;
- }
- close HTPASSWD_TMP;
- rename "$opt_h.tmp", "$opt_h" or die $!;
- flock(HTPASSWD,LOCK_UN);
- close HTPASSWD;
-}
-
-open(MAPSECRETS,"<$FREESIDE_CONF/mapsecrets")
- and flock(MAPSECRETS,LOCK_EX)
- or die "can't open $FREESIDE_CONF/mapsecrets: $!";
-open(MAPSECRETS_TMP,">>$FREESIDE_CONF/mapsecrets.tmp")
- or die "can't open $FREESIDE_CONF/mapsecrets.tmp: $!";
-while (<MAPSECRETS>) {
- print MAPSECRETS_TMP $_ unless /^$user\s/;
-}
-close MAPSECRETS_TMP;
-rename "$FREESIDE_CONF/mapsecrets.tmp", "$FREESIDE_CONF/mapsecrets" or die $!;
-flock(MAPSECRETS,LOCK_UN);
-close MAPSECRETS;
-
-sub usage {
- die "Usage:\n\n freeside-deluser [ -h htpasswd_file ] username"
-}
-
-=head1 NAME
-
-freeside-deluser - Command line interface to add (freeside) users.
-
-=head1 SYNOPSIS
-
- freeside-deluser [ -h htpasswd_file ] username
-
-=head1 DESCRIPTION
-
-Adds a user to the Freeside billing system. This is for adding users (internal
-sales/tech folks) to the web interface, not for adding customer accounts.
-
- -h: Also delete from the given htpasswd filename
-
-=head1 SEE ALSO
-
-L<freeside-adduser>, L<htpasswd>(1), base Freeside documentation
-
-=cut
-
diff --git a/FS/bin/freeside-eftca-download b/FS/bin/freeside-eftca-download
index 702a80ca1..1b7653cb3 100755
--- a/FS/bin/freeside-eftca-download
+++ b/FS/bin/freeside-eftca-download
@@ -52,7 +52,7 @@ my $conf = new FS::Conf;
my @agents;
if ( $conf->exists('batch-spoolagent') ) {
- @agents = qsearchs('agent', { 'disabled' => '' });
+ @agents = qsearch('agent', { 'disabled' => '' });
} else {
@agents = (1);
}
@@ -62,11 +62,14 @@ foreach my $agent (@agents) {
my @batchconf;
if ( $conf->exists('batch-spoolagent') ) {
@batchconf = $conf->config('batchconfig-eft_canada', $agent->agentnum, 1);
- next unless $batchconf[0];
+ if ( !length($batchconf[0]) ) {
+ warn "agent '".$agent->agent."' has no batchconfig-eft_canada setting; skipped.\n";
+ next;
+ }
} else {
@batchconf = $conf->config('batchconfig-eft_canada');
}
- # BIN, terminalID, merchantID, username, password
+ # user, password, transaction code, delay days
my $user = $batchconf[0] or die "no EFT Canada batch username configured\n";
my $pass = $batchconf[1] or die "no EFT Canada batch password configured\n";
@@ -82,7 +85,7 @@ foreach my $agent (@agents) {
$sftp->setcwd('/Returns');
- my $files = $sftp->ls('.', wanted => qr/^ReturnFile/, names_only => 1);
+ my $files = $sftp->ls('.', wanted => qr/\.txt$/, names_only => 1);
die "no response files found\n" if !@$files;
FILE: foreach my $filename (@$files) {
diff --git a/FS/bin/freeside-email b/FS/bin/freeside-email
index 7a93f78ee..6e4e0fe6c 100755
--- a/FS/bin/freeside-email
+++ b/FS/bin/freeside-email
@@ -45,7 +45,7 @@ freeside-email - Prints email addresses of all users on STDOUT
Prints the email addresses of all customers on STDOUT, separated by newlines.
-user: From the mapsecrets file - see config.html from the base documentation
+user: Freeside user
=head1 BUGS
diff --git a/FS/bin/freeside-fetch b/FS/bin/freeside-fetch
index f689bfd93..c1ab78373 100755
--- a/FS/bin/freeside-fetch
+++ b/FS/bin/freeside-fetch
@@ -79,7 +79,7 @@ freeside-fetch - Send a freeside page to a list of employees.
Fetches a web page specified by url as if employee and emails it to
employee. Useful when run out of cron to send freeside web pages.
- user: From the mapsecrets file - a user with access to the freeside database
+ user: Freeside user
employee: the username of an employee to receive the emailed page. May be a comma separated list
diff --git a/FS/bin/freeside-ipifony-download b/FS/bin/freeside-ipifony-download
new file mode 100644
index 000000000..9df4db08a
--- /dev/null
+++ b/FS/bin/freeside-ipifony-download
@@ -0,0 +1,320 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Std;
+use Date::Format qw(time2str);
+use File::Temp qw(tempdir);
+use Net::SFTP::Foreign;
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearch qsearchs);
+use FS::cust_main;
+use FS::Conf;
+use File::Copy qw(copy);
+use Text::CSV;
+
+my %opt;
+getopts('va:P:C:e:', \%opt);
+
+# Product codes that are subject to flat rate E911 charges. For these
+# products, the'quantity' field represents the number of lines.
+my @E911_CODES = ( 'V-HPBX', 'V-TRUNK' );
+
+# Map TAXNONVOICE/TAXVOICE to Freeside taxclass names
+my %TAXCLASSES = (
+ 'TAXNONVOICE' => 'Other',
+ 'TAXVOICE' => 'VoIP',
+);
+
+
+#$Net::SFTP::Foreign::debug = -1;
+sub HELP_MESSAGE { '
+ Usage:
+ freeside-ipifony-download
+ [ -v ]
+ [ -a archivedir ]
+ [ -P port ]
+ [ -C category ]
+ [ -e pkgpart ]
+ freesideuser sftpuser@hostname[:path]
+' }
+
+my @fields = (
+ 'custnum',
+ 'date_desc',
+ 'quantity',
+ 'unit_price',
+ 'classname',
+ 'taxclass',
+);
+
+my $user = shift or die &HELP_MESSAGE;
+my $dbh = adminsuidsetup $user;
+$FS::UID::AutoCommit = 0;
+
+# for statistics
+my $num_charges = 0;
+my $num_errors = 0;
+my $sum_charges = 0;
+# cache classnums
+my %classnum_of;
+
+if ( $opt{a} ) {
+ die "no such directory: $opt{a}\n"
+ unless -d $opt{a};
+ die "archive directory $opt{a} is not writable by the freeside user\n"
+ unless -w $opt{a};
+}
+
+my $e911_part_pkg;
+if ( $opt{e} ) {
+ $e911_part_pkg = FS::part_pkg->by_key($opt{e})
+ or die "E911 pkgpart $opt{e} not found.\n";
+
+ if ( $e911_part_pkg->base_recur > 0 or $e911_part_pkg->freq ) {
+ die "E911 pkgpart $opt{e} must be a one-time charge.\n";
+ }
+}
+
+my $categorynum = '';
+if ( $opt{C} ) {
+ # find this category (don't auto-create it, it should exist already)
+ my $category = qsearchs('pkg_category', { categoryname => $opt{C} });
+ if (!defined($category)) {
+ die "Package category '$opt{C}' does not exist.\n";
+ }
+ $categorynum = $category->categorynum;
+}
+
+#my $tmpdir = File::Temp->newdir();
+my $tmpdir = tempdir( CLEANUP => 1 ); #DIR=>somewhere?
+
+my $host = shift
+ or die &HELP_MESSAGE;
+my ($sftpuser, $path);
+$host =~ s/^(.+)\@//;
+$sftpuser = $1 || $ENV{USER};
+$host =~ s/:(.*)//;
+$path = $1;
+
+my $port = 22;
+if ( $opt{P} =~ /^(\d+)$/ ) {
+ $port = $1;
+}
+
+# for now assume SFTP download as the only method
+print STDERR "Connecting to $sftpuser\@$host...\n" if $opt{v};
+
+my $sftp = Net::SFTP::Foreign->new(
+ host => $host,
+ user => $sftpuser,
+ port => $port,
+ # for now we don't support passwords. use authorized_keys.
+ timeout => 30,
+ #more => ($opt{v} ? '-v' : ''),
+);
+die "failed to connect to '$sftpuser\@$host'\n(".$sftp->error.")\n"
+ if $sftp->error;
+
+$sftp->setcwd($path) if $path;
+
+my $files = $sftp->ls('ready', wanted => qr/\.csv$/, names_only => 1);
+if (!@$files) {
+ print STDERR "No charge files found.\n" if $opt{v};
+ exit(-1);
+}
+
+my %cust_main; # cache
+my %e911_qty; # custnum => sum of E911-subject quantity
+
+my %is_e911 = map {$_ => 1} @E911_CODES;
+
+FILE: foreach my $filename (@$files) {
+ print STDERR "Retrieving $filename\n" if $opt{v};
+ $sftp->get("ready/$filename", "$tmpdir/$filename");
+ if($sftp->error) {
+ warn "failed to download $filename\n";
+ next FILE;
+ }
+
+ # make sure server archive dir exists
+ if ( !$sftp->stat('done') ) {
+ print STDERR "Creating $path/done\n" if $opt{v};
+ $sftp->mkdir('done');
+ if($sftp->error) {
+ # something is seriously wrong
+ die "failed to create archive directory on server:\n".$sftp->error."\n";
+ }
+ }
+ #move to server archive dir
+ $sftp->rename("ready/$filename", "done/$filename");
+ if($sftp->error) {
+ warn "failed to archive $filename on server:\n".$sftp->error."\n";
+ } # process it anyway, I guess/
+
+ #copy to local archive dir
+ if ( $opt{a} ) {
+ print STDERR "Copying $tmpdir/$filename to archive dir $opt{a}\n"
+ if $opt{v};
+ copy("$tmpdir/$filename", $opt{a});
+ warn "failed to copy $tmpdir/$filename to $opt{a}: $!" if $!;
+ }
+
+ open my $fh, "<$tmpdir/$filename";
+ my $csv = Text::CSV->new; # orthodox CSV
+ my %hash;
+ while (my $line = <$fh>) {
+ $csv->parse($line) or do {
+ warn "can't parse $filename: ".$csv->error_input."\n";
+ next FILE;
+ };
+ @hash{@fields} = $csv->fields();
+ if ( $hash{custnum} =~ /^cust/ ) {
+ # there appears to be a header row
+ print STDERR "skipping header row\n" if $opt{v};
+ next;
+ }
+ my $cust_main =
+ $cust_main{$hash{custnum}} ||= FS::cust_main->by_key($hash{custnum});
+ if (!$cust_main) {
+ warn "customer #$hash{custnum} not found\n";
+ next;
+ }
+ print STDERR "Found customer #$hash{custnum}: ".$cust_main->name."\n"
+ if $opt{v};
+
+ my $amount = sprintf('%.2f',$hash{quantity} * $hash{unit_price});
+ # construct arguments for $cust_main->charge
+ my %charge_opt = (
+ amount => $hash{unit_price},
+ quantity => $hash{quantity},
+ start_date => $cust_main->next_bill_date,
+ pkg => $hash{date_desc} .
+ ' (' . $hash{quantity} . ' @ $' . $hash{unit_price} . ' ea)',
+ taxclass => $TAXCLASSES{ $hash{taxclass} },
+ );
+ if (my $classname = $hash{classname}) {
+ if (!exists($classnum_of{$classname}) ) {
+ # then look it up
+ my $pkg_class = qsearchs('pkg_class', {
+ classname => $classname,
+ categorynum => $categorynum,
+ });
+ if (!defined($pkg_class)) {
+ # then create it
+ $pkg_class = FS::pkg_class->new({
+ classname => $classname,
+ categorynum => $categorynum,
+ });
+ my $error = $pkg_class->insert;
+ die "Error creating package class for product code '$classname':\n".
+ "$error\n"
+ if $error;
+ }
+
+ $classnum_of{$classname} = $pkg_class->classnum;
+ }
+ $charge_opt{classnum} = $classnum_of{$classname};
+ }
+ print STDERR " Charging $hash{unit_price} * $hash{quantity}\n"
+ if $opt{v};
+ my $error = $cust_main->charge(\%charge_opt);
+ if ($error) {
+ warn "Error creating charge: $error" if $error;
+ $num_errors++;
+ } else {
+ $num_charges++;
+ $sum_charges += $amount;
+ }
+
+ if ( $opt{e} and $is_e911{$hash{classname}} ) {
+ $e911_qty{$hash{custnum}} ||= 0;
+ $e911_qty{$hash{custnum}} += $hash{quantity};
+ }
+ } #while $line
+ close $fh;
+} #FILE
+
+# Order E911 packages
+my $num_e911 = 0;
+my $num_lines = 0;
+foreach my $custnum ( keys (%e911_qty) ) {
+ my $cust_main = $cust_main{$custnum};
+ my $quantity = $e911_qty{$custnum};
+ next if $quantity == 0;
+ my $cust_pkg = FS::cust_pkg->new({
+ pkgpart => $opt{e},
+ custnum => $custnum,
+ start_date => $cust_main->next_bill_date,
+ quantity => $quantity,
+ });
+ my $error = $cust_main->order_pkg({ cust_pkg => $cust_pkg });
+ if ( $error ) {
+ warn "Error creating e911 charge for customer $custnum: $error\n";
+ $num_errors++;
+ } else {
+ $num_e911++;
+ $num_lines += $quantity;
+ }
+}
+
+$dbh->commit;
+
+if ($opt{v}) {
+ print STDERR "
+Finished!
+ Processed files: @$files
+ Created charges: $num_charges
+ Sum of charges: \$".sprintf('%0.2f', $sum_charges)."
+ E911 charges: $num_e911
+ E911 lines: $num_lines
+ Errors: $num_errors
+";
+}
+
+=head1 NAME
+
+freeside-ipifony-download - Download and import invoice items from IPifony.
+
+=head1 SYNOPSIS
+
+ freeside-ipifony-download
+ [ -v ]
+ [ -a archivedir ]
+ [ -P port ]
+ [ -C category ]
+ [ -T taxclass ]
+ [ -e pkgpart ]
+ freesideuser sftpuser@hostname[:path]
+
+=head1 REQUIRED PARAMETERS
+
+I<freesideuser>: the Freeside user to run as.
+
+I<sftpuser>: the SFTP user to connect as. The 'freeside' system user should
+have an authorization key to connect as that user.
+
+I<hostname>: the SFTP server.
+
+=head1 OPTIONAL PARAMETERS
+
+-v: Be verbose.
+
+-a I<archivedir>: Save a copy of the downloaded file to I<archivedir>.
+
+-P I<port>: Connect to that TCP port.
+
+-C I<category>: The name of a package category to use when creating package
+classes.
+
+-e I<pkgpart>: The pkgpart (L<FS::part_pkg>) to use for E911 charges. A
+package of this type will be ordered for each invoice that has E911-subject
+line items. The 'quantity' field on this package will be set to the total
+quantity of those line items.
+
+The E911 package must be a one-time package (flat rate, no frequency, no
+recurring fee) with setup fee equal to the fee per line.
+
+=cut
+
+1;
+
diff --git a/FS/bin/freeside-monthly b/FS/bin/freeside-monthly
index 0d6ea14a2..431fbd86f 100755
--- a/FS/bin/freeside-monthly
+++ b/FS/bin/freeside-monthly
@@ -7,7 +7,7 @@ use FS::UID qw(adminsuidsetup);
&untaint_argv; #what it sounds like (eww)
#use vars qw($opt_d $opt_v $opt_p $opt_a $opt_s $opt_y);
use vars qw(%opt);
-getopts("p:a:d:vsy:", \%opt);
+getopts("p:a:d:vsy:m", \%opt);
my $user = shift or die &usage;
adminsuidsetup $user;
@@ -72,7 +72,9 @@ the bill and collect methods of a cust_main object. See L<FS::cust_main>.
-v: enable debugging
-user: From the mapsecrets file - see config.html from the base documentation
+ -m: Experimental multi-process mode (delay upload jobs until billing jobs complete)
+
+user: Typically "fs_daily"
custnum: if one or more customer numbers are specified, only bills those
customers. Otherwise, bills all customers.
diff --git a/FS/bin/freeside-phonenum_list b/FS/bin/freeside-phonenum_list
new file mode 100755
index 000000000..19b564dee
--- /dev/null
+++ b/FS/bin/freeside-phonenum_list
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use vars qw( $opt_c $opt_o $opt_l $opt_p $opt_b $opt_d $opt_s $opt_t );
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::svc_phone;
+
+getopts('colp:b:d:s:t:');
+
+my $user = shift or &usage;
+adminsuidsetup $user;
+
+my $conf = new FS::Conf;
+my $default_locale = $conf->config('locale') || 'en_US';
+
+my %search = ();
+
+$search{payby} = [ split(/\s*,\s*/, $opt_p) ] if $opt_p;
+$search{balance} = $opt_b if $opt_b;
+$search{balance_days} = $opt_d if $opt_d;
+$search{svcpart} = [ split(/\s*,\s*/, $opt_s) ] if $opt_s;
+$search{cust_status} = lc($opt_t) if $opt_t;
+
+my @svc_phone = qsearch( FS::svc_phone->search(\%search) );
+
+foreach my $svc_phone (@svc_phone) {
+ print $svc_phone->countrycode if $opt_c;
+ print $svc_phone->phonenum;
+ print '@'. $svc_phone->domain if $opt_o;
+ if ( $opt_l ) {
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+ print ','. ($cust_pkg && $cust_pkg->cust_main->locale || $default_locale);
+ }
+ print "\n";
+}
+
+sub usage {
+ die "usage: freeside-phonenum_list [ -c ] [ -o ] [ -l ] [ -p payby,payby... ] [ -b balance [ -d balance_days ] ] [ -s svcpart,svcpart... ] username \n";
+}
+
+=head1 NAME
+
+freeside-phonenum_list
+
+=head1 SYNOPSIS
+ freeside-phonenum_list [ -c ] [ -o ] [ -l ] [ -p payby,payby... ] [ -b balance [ -d balance_days ] ] [ -s svcpart,svcpart... ] username
+
+=head1 DESCRIPTION
+
+Command-line tool to list phone numbers.
+
+Display options:
+
+-c: Include country code
+
+-o: Include domain
+
+-l: Include customer locale
+
+Selection options:
+
+-p: Customer payby (CARD, BILL, etc.). Separate multiple values with commas.
+
+-b: Customer balance over (or equal to) this amount
+
+-d: Customer balance age over this many days
+
+-s: Service definition (svcpart). Separate multiple values with commas.
+
+-t: Customer status: prospect, active, ordered, inactive, suspended or cancelled
+
+username: Employee username
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_phone>, L<FS::cust_main>
+
+=cut
+
+1;
+
diff --git a/FS/bin/freeside-queued b/FS/bin/freeside-queued
index 756b699d4..5eac06b24 100644
--- a/FS/bin/freeside-queued
+++ b/FS/bin/freeside-queued
@@ -11,6 +11,7 @@ use FS::Conf;
use FS::Record qw(qsearch);
use FS::queue;
use FS::queue_depend;
+use FS::Log;
# no autoloading for non-FS classes...
use Net::SSH 0.07;
@@ -45,6 +46,7 @@ while ( $@ ) {
}
}
+my $log = FS::Log->new('queue');
logfile( "%%%FREESIDE_LOG%%%/queuelog.". $FS::UID::datasrc );
warn "completing daemonization (detaching))\n" if $DEBUG;
@@ -135,6 +137,8 @@ while (1) {
foreach my $job ( @jobs ) {
+ $log->debug('locking queue job', object => $job);
+
my %hash = $job->hash;
$hash{'status'} = 'locked';
my $ljob = new FS::queue ( \%hash );
@@ -186,7 +190,7 @@ while (1) {
dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
#auto-use classes...
- if ( $ljob->job =~ /(FS::(part_export|cust_main|cust_pkg)::\w+)::/
+ if ( $ljob->job =~ /(FS::(part_export|cust_main|cust_pkg|Cron)::\w+)::/
|| $ljob->job =~ /(FS::\w+)::/
)
{
@@ -205,9 +209,13 @@ while (1) {
}
my $eval = "&". $ljob->job. '(@args);';
+ # don't put @args in the log, may expose passwords
+ $log->info('starting job ('.$ljob->job.')');
warn 'running "&'. $ljob->job. '('. join(', ', @args). ")\n" if $DEBUG;
+ local $FS::UID::AutoCommit = 0; # so that we can clean up failures
eval $eval; #throw away return value? suppose so
if ( $@ ) {
+ dbh->rollback;
my %hash = $ljob->hash;
$hash{'statustext'} = $@;
if ( $hash{'statustext'} =~ /\/misc\/queued_report/ ) { #use return?
@@ -219,8 +227,10 @@ while (1) {
my $fjob = new FS::queue( \%hash );
my $error = $fjob->replace($ljob);
die $error if $error;
+ dbh->commit; # for the status change only
} else {
$ljob->delete;
+ dbh->commit; # for the job itself
}
if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
@@ -286,7 +296,7 @@ Job queue daemon. Should be running at all times.
-n: non-"secure" jobs only (other jobs)
-user: from the mapsecrets file - see config.html from the base documentation
+user: Typically "fs_queue"
=head1 VERSION
diff --git a/FS/bin/freeside-selfservice-server b/FS/bin/freeside-selfservice-server
index c10623c96..8ce74d5c8 100644
--- a/FS/bin/freeside-selfservice-server
+++ b/FS/bin/freeside-selfservice-server
@@ -16,6 +16,7 @@ use FS::UID qw(adminsuidsetup forksuidsetup);
use FS::ClientAPI qw( load_clientapi_modules );
use FS::ClientAPI_SessionCache;
use FS::Record qw( qsearch qsearchs );
+use FS::TicketSystem;
use FS::Conf;
use FS::cust_svc;
@@ -108,31 +109,7 @@ while (1) {
if ( $keepalives && $keepalive_count++ > 10 ) {
$keepalive_count = 0;
lock_write;
-
nstore_fd( { _token => '_keepalive' }, $writer );
-
-#commenting izoom stuff out until we can move it to a branch (or just remove)
-# foreach my $agent ( qsearch( 'agent', { disabled => '' } ) ) {
-# my $config = qsearchs( 'conf', { name => 'selfservice-bulk_ftp_dir',
-# agentnum => $agent->agentnum,
-# } )
-# or next;
-#
-# my $session =
-# FS::ClientAPI->dispatch( 'Agent/agent_login',
-# { username => $agent->username,
-# password => $agent->_password,
-# }
-# );
-#
-# nstore_fd( { _token => '_ftp_scan',
-# dir => $config->value,
-# session_id => $session->{session_id},
-# },
-# $writer
-# );
-# }
-
unlock_write;
}
next;
@@ -181,12 +158,10 @@ while (1) {
warn "child $pid spawned\n" if $Debug;
} else { #kid time
- ##get new db handle
$FS::UID::dbh->{InactiveDestroy} = 1;
forksuidsetup($user);
- #get db handle
- #adminsuidsetup($user);
+ FS::TicketSystem->init();
my $type = $packet->{_packet};
warn "calling $type handler\n" if $Debug;
diff --git a/FS/bin/freeside-selfservice-xmlrpcd b/FS/bin/freeside-selfservice-xmlrpcd
index acf516abe..423d2c30b 100755
--- a/FS/bin/freeside-selfservice-xmlrpcd
+++ b/FS/bin/freeside-selfservice-xmlrpcd
@@ -28,6 +28,7 @@ use FS::UID qw( adminsuidsetup forksuidsetup dbh );
use FS::Conf;
use FS::ClientAPI qw( load_clientapi_modules );
use FS::ClientAPI_XMLRPC; #FS::SelfService::XMLRPC;
+use FS::TicketSystem;
#freeside
my $FREESIDE_LOG = "%%%FREESIDE_LOG%%%";
@@ -195,6 +196,9 @@ sub server_do_fork {
#freeside db connection, etc.
forksuidsetup($user);
+ #why isn't this needed ala freeside-selfservice-server??
+ #FS::TicketSystem->init();
+
return;
}
}
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
index 155c74aa0..07da88dea 100755
--- a/FS/bin/freeside-setup
+++ b/FS/bin/freeside-setup
@@ -32,7 +32,7 @@ $config_dir =~ /^([\w.:=\/]+)$/
or die "unacceptable configuration directory name";
$config_dir = $1;
-getsecrets($opt_u);
+getsecrets();
#needs to match FS::Record
my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
diff --git a/FS/bin/freeside-upgrade b/FS/bin/freeside-upgrade
index b08a8401f..5bd141538 100755
--- a/FS/bin/freeside-upgrade
+++ b/FS/bin/freeside-upgrade
@@ -5,7 +5,7 @@ use vars qw($opt_d $opt_s $opt_q $opt_v $opt_r);
use vars qw($DEBUG $DRY_RUN);
use Getopt::Std;
use DBIx::DBSchema 0.31; #0.39
-use FS::UID qw(adminsuidsetup checkeuid datasrc driver_name); #getsecrets);
+use FS::UID qw(adminsuidsetup checkeuid datasrc driver_name);
use FS::CurrentUser;
use FS::Schema qw( dbdef dbdef_dist reload_dbdef );
use FS::Misc::prune qw(prune_applications);
@@ -123,6 +123,8 @@ my $cf;
while ( $cf = $cfsth->fetchrow_hashref ) {
my $tbl = $cf->{'dbtable'};
my $name = $cf->{'name'};
+ $name = lc($name) unless driver_name =~ /^mysql/i;
+
@statements = grep { $_ !~ /^\s*ALTER\s+TABLE\s+(h_|)$tbl\s+DROP\s+COLUMN\s+cf_$name\s*$/i }
@statements;
push @statements,
diff --git a/FS/bin/freeside-username_list b/FS/bin/freeside-username_list
new file mode 100755
index 000000000..5352f02eb
--- /dev/null
+++ b/FS/bin/freeside-username_list
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+use strict;
+use vars qw( $opt_o $opt_l $opt_p $opt_b $opt_d $opt_s $opt_t );
+use Getopt::Std;
+use FS::UID qw(adminsuidsetup);
+use FS::Conf;
+use FS::Record qw(qsearch);
+use FS::svc_acct;
+
+getopts('olp:b:d:s:t:');
+
+my $user = shift or &usage;
+adminsuidsetup $user;
+
+my $conf = new FS::Conf;
+my $default_locale = $conf->config('locale') || 'en_US';
+
+my %search = ();
+
+$search{payby} = [ split(/\s*,\s*/, $opt_p) ] if $opt_p;
+$search{balance} = $opt_b if $opt_b;
+$search{balance_days} = $opt_d if $opt_d;
+$search{svcpart} = [ split(/\s*,\s*/, $opt_s) ] if $opt_s;
+$search{cust_status} = lc($opt_t) if $opt_t;
+
+my @svc_acct = qsearch( FS::svc_acct->search(\%search) );
+
+foreach my $svc_acct (@svc_acct) {
+ print $svc_acct->username;
+ print '@'. $svc_acct->domain if $opt_o;
+ if ( $opt_l ) {
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ print ','. ($cust_pkg && $cust_pkg->cust_main->locale || $default_locale);
+ }
+ print "\n";
+}
+
+sub usage {
+ die "usage: freeside-username_list [ -c ] [ -l ] [ -p payby,payby... ] [ -b balance [ -d balance_days ] ] [ -s svcpart,svcpart... ] username \n";
+}
+
+=head1 NAME
+
+freeside-username_list
+
+=head1 SYNOPSIS
+
+ freeside-username_list [ -c ] [ -l ] [ -p payby,payby... ] [ -b balance [ -d balance_days ] ] [ -s svcpart,svcpart... ] username
+
+=head1 DESCRIPTION
+
+Command-line tool to list usernames.
+
+Display options:
+
+-o: Include domain
+
+-l: Include customer locale
+
+Selection options:
+
+-p: Customer payby (CARD, BILL, etc.). Separate multiple values with commas.
+
+-b: Customer balance over (or equal to) this amount
+
+-d: Customer balance age over this many days
+
+-s: Service definition (svcpart). Separate multiple values with commas.
+
+-t: Customer status: prospect, active, ordered, inactive, suspended or cancelled
+
+username: Employee username
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_acct>, L<FS::cust_main>
+
+=cut
+
+1;
+
diff --git a/FS/bin/freeside-void-payments b/FS/bin/freeside-void-payments
index 8c1f3dbdf..49b74d388 100755
--- a/FS/bin/freeside-void-payments
+++ b/FS/bin/freeside-void-payments
@@ -90,8 +90,11 @@ my $notfound = 0;
my $canceled = 0;
print "Voiding ".scalar(@auths)." transactions:\n" if $opt{'v'};
foreach my $authnum (@auths) {
- my $paybatch = $gatewaynum . $processor . ':' . $authnum;
- my $cust_pay = qsearchs('cust_pay', { paybatch => $paybatch } );
+ my $cust_pay = qsearchs('cust_pay', {
+ gatewaynum => $gatewaynum,
+ processor => $processor,
+ authorization => $authnum,
+ });
my $error;
my $cancel_error;
if($cust_pay) {
@@ -103,7 +106,11 @@ foreach my $authnum (@auths) {
}
}
else {
- my $cpv = qsearchs('cust_pay_void', { paybatch => $paybatch });
+ my $cpv = qsearchs('cust_pay_void', {
+ gatewaynum => $gatewaynum,
+ processor => $processor,
+ authorization => $authnum,
+ });
if($cpv) {
$error = 'already voided '.time2str('%Y-%m-%d', $cpv->void_date) .
' by ' . $cpv->otaker;
diff --git a/FS/bin/freeside-wkhtmltopdf b/FS/bin/freeside-wkhtmltopdf
index c6c5531a5..f0c53e6da 100755
--- a/FS/bin/freeside-wkhtmltopdf
+++ b/FS/bin/freeside-wkhtmltopdf
@@ -1,7 +1,7 @@
#!/bin/sh
-if [ $DISPLAY ] ; then
- wkhtmltopdf $@
-else
+#if [ $DISPLAY ] ; then
+# wkhtmltopdf $@
+#else
xvfb-run -- wkhtmltopdf $@
-fi
+#fi