diff options
author | jeff <jeff> | 2009-08-12 14:58:50 +0000 |
---|---|---|
committer | jeff <jeff> | 2009-08-12 14:58:50 +0000 |
commit | 56a2965996454a0649d43ecbc062beda61106e21 (patch) | |
tree | 12c4c2167844a44b09ff5da4cf2bf128cdcf2bc7 /FS | |
parent | 600a15aa56c212e1206fced9cbcf9de103c2f95b (diff) |
internalize billco-upload and automate the transfer to the provider RT#5902
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/Conf.pm | 72 | ||||
-rw-r--r-- | FS/FS/Cron/upload.pm | 176 | ||||
-rwxr-xr-x | FS/bin/freeside-daily | 4 | ||||
-rwxr-xr-x | FS/bin/freeside-monthly | 3 |
4 files changed, 240 insertions, 15 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 1da55837c..66d8be903 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -76,11 +76,23 @@ sub base_dir { $1; } -=item config KEY [ AGENTNUM ] +=item conf KEY [ AGENTNUM [ NODEFAULT ] ] + +Returns the L<FS::conf> record for the key and agent. + +=cut + +sub conf { + my $self = shift; + $self->_config(@_); +} + +=item config KEY [ AGENTNUM [ NODEFAULT ] ] Returns the configuration value or values (depending on context) for key. The optional agent number selects an agent specific value instead of the -global default if one is present. +global default if one is present. If NODEFAULT is true only the agent +specific value(s) is returned. =cut @@ -92,14 +104,13 @@ sub _usecompat { $compat->$method(@_); } -# needs a non _ name, called externally by config-view now (and elsewhere?) sub _config { - my($self,$name,$agentnum)=@_; + my($self,$name,$agentnum,$agentonly)=@_; my $hashref = { 'name' => $name }; $hashref->{agentnum} = $agentnum; local $FS::Record::conf = undef; # XXX evil hack prevents recursion my $cv = FS::Record::qsearchs('conf', $hashref); - if (!$cv && defined($agentnum) && $agentnum) { + if (!$agentonly && !$cv && defined($agentnum) && $agentnum) { $hashref->{agentnum} = ''; $cv = FS::Record::qsearchs('conf', $hashref); } @@ -110,12 +121,10 @@ sub config { my $self = shift; return $self->_usecompat('config', @_) if use_confcompat; - my($name, $agentnum)=@_; - - carp "FS::Conf->config($name, $agentnum) called" + carp "FS::Conf->config(". join(', ', @_). ") called" if $DEBUG > 1; - my $cv = $self->_config($name, $agentnum) or return; + my $cv = $self->_config(@_) or return; if ( wantarray ) { my $v = $cv->value; @@ -126,7 +135,7 @@ sub config { } } -=item config_binary KEY [ AGENTNUM ] +=item config_binary KEY [ AGENTNUM [ NODEFAULT ] ] Returns the exact scalar value for key. @@ -136,12 +145,11 @@ sub config_binary { my $self = shift; return $self->_usecompat('config_binary', @_) if use_confcompat; - my($name,$agentnum)=@_; - my $cv = $self->_config($name, $agentnum) or return; + my $cv = $self->_config(@_) or return; decode_base64($cv->value); } -=item exists KEY [ AGENTNUM ] +=item exists KEY [ AGENTNUM [ NODEFAULT ] ] Returns true if the specified key exists, even if the corresponding value is undefined. @@ -154,10 +162,10 @@ sub exists { my($name, $agentnum)=@_; - carp "FS::Conf->exists($name, $agentnum) called" + carp "FS::Conf->exists(". join(', ', @_). ") called" if $DEBUG > 1; - defined($self->_config($name, $agentnum)); + defined($self->_config(@_)); } =item config_orbase KEY SUFFIX @@ -620,6 +628,40 @@ worry that config_items is freeside-specific and icky. }, { + 'key' => 'billco-url', + 'section' => 'billing', + 'description' => 'The url to use for performing uploads to the invoice mailing service.', + 'type' => 'text', + 'per_agent' => 1, + }, + + { + 'key' => 'billco-username', + 'section' => 'billing', + 'description' => 'The login name to use for uploads to the invoice mailing service.', + 'type' => 'text', + 'per_agent' => 1, + 'agentonly' => 1, + }, + + { + 'key' => 'billco-password', + 'section' => 'billing', + 'description' => 'The password to use for uploads to the invoice mailing service.', + 'type' => 'text', + 'per_agent' => 1, + 'agentonly' => 1, + }, + + { + 'key' => 'billco-clicode', + 'section' => 'billing', + 'description' => 'The clicode to use for uploads to the invoice mailing service.', + 'type' => 'text', + 'per_agent' => 1, + }, + + { 'key' => 'business-onlinepayment', 'section' => 'billing', 'description' => '<a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support, at least three lines: processor, login, and password. An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\'). Optional additional lines are passed to Business::OnlinePayment as %processor_options.', diff --git a/FS/FS/Cron/upload.pm b/FS/FS/Cron/upload.pm new file mode 100644 index 000000000..fea3d2cc7 --- /dev/null +++ b/FS/FS/Cron/upload.pm @@ -0,0 +1,176 @@ +package FS::Cron::upload; + +use strict; +use vars qw( @ISA @EXPORT_OK $me $DEBUG ); +use Exporter; +use Date::Format; +use FS::UID qw(dbh); +use FS::Record qw( qsearch qsearchs ); +use FS::Conf; +use FS::queue; +use FS::agent; +use LWP::UserAgent; +use HTTP::Request; +use HTTP::Request::Common; +use HTTP::Response; + +@ISA = qw( Exporter ); +@EXPORT_OK = qw ( upload ); +$DEBUG = 0; +$me = '[FS::Cron::upload]'; + +#freeside-daily %opt: +# -v: enable debugging +# -l: debugging level +# -m: Experimental multi-process mode uses the job queue for multi-process and/or multi-machine billing. +# -r: Multi-process mode dry run option +# -a: Only process customers with the specified agentnum + + +sub upload { + my %opt = @_; + + my $debug = 0; + $debug = 1 if $opt{'v'}; + $debug = $opt{'l'} if $opt{'l'}; + + local $DEBUG = $debug if $debug; + + warn "$me upload called\n" if $DEBUG; + + my $conf = new FS::Conf; + my @agent = grep { $conf->config( 'billco-username', $_->agentnum, 1 ) } + grep { $conf->config( 'billco-password', $_->agentnum, 1 ) } + qsearch( 'agent', {} ); + + my $date = time2str('%Y%m%d%H%M%S', $^T); # more? + + @agent = grep { $_ == $opt{'a'} } @agent if $opt{'a'}; + + foreach my $agent ( @agent ) { + + my $agentnum = $agent->agentnum; + + if ( $opt{'m'} ) { + + if ( $opt{'r'} ) { + warn "DRY RUN: would add agent $agentnum for queued upload\n"; + } else { + + my $queue = new FS::queue { + 'job' => 'FS::Cron::upload::billco_upload', + }; + my $error = $queue->insert( + 'agentnum' => $agentnum, + 'date' => $date, + 'l' => $opt{'l'} || '', + 'm' => $opt{'m'} || '', + 'v' => $opt{'v'} || '', + ); + + } + + } else { + + eval "&billco_upload( 'agentnum' => $agentnum, 'date' => $date );"; + warn "billco_upload failed: $@\n" + if ( $@ ); + + } + + } + +} + +sub billco_upload { + my %opt = @_; + + warn "$me billco_upload called\n" if $DEBUG; + my $conf = new FS::Conf; + my $dir = '%%%FREESIDE_EXPORT%%%/export.'. $FS::UID::datasrc. '/cust_bill'; + + my $agentnum = $opt{agentnum} or die "no agentnum provided\n"; + my $url = $conf->config( 'billco-url', $agentnum ) + or die "no url for agent $agentnum\n"; + my $username = $conf->config( 'billco-username', $agentnum, 1 ) + or die "no username for agent $agentnum\n"; + my $password = $conf->config( 'billco-password', $agentnum, 1 ) + or die "no password for agent $agentnum\n"; + my $clicode = $conf->config( 'billco-clicode', $agentnum ) + or die "no clicode for agent $agentnum\n"; + + die "no date provided\n" unless $opt{date}; + my $zipfile = "$dir/agentnum$agentnum-$opt{date}.zip"; + + 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 $agent = qsearchs( 'agent', { agentnum => $agentnum } ) + or die "no such agent: $agentnum"; + $agent->select_for_update; #mutex + + unless ( -f "$dir/agentnum$agentnum-header.csv" || + -f "$dir/agentnum$agentnum-detail.csv" ) + { + warn "$me neither $dir/agentnum$agentnum-header.csv nor ". + "$dir/agentnum$agentnum-detail.csv found\n" if $DEBUG; + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + return; + } + + # a better way? + if ($opt{m}) { + my $sql = "SELECT count(*) FROM queue LEFT JOIN cust_main USING(custnum) ". + "WHERE queue.job='FS::cust_main::queued_bill' AND cust_main.agentnum = ?"; + my $sth = $dbh->prepare($sql) or die $dbh->errstr; + while (1) { + $sth->execute( $agentnum ) + or die "Unexpected error executing statement $sql: ". $sth->errstr; + last if $sth->fetchow_arrayref->[0]; + sleep 300; + } + } + + foreach ( qw ( header detail ) ) { + rename "$dir/agentnum$agentnum-$_.csv", + "$dir/agentnum$agentnum-$opt{date}-$_.csv"; + } + + my $command = "cd $dir; zip $zipfile ". + "agentnum$agentnum-$opt{date}-header.csv ". + "agentnum$agentnum-$opt{date}-detail.csv"; + + system($command) and die "$command failed\n"; + + unlink "agentnum$agentnum-$opt{date}-header.csv", + "agentnum$agentnum-$opt{date}-detail.csv"; + + my $ua = new LWP::UserAgent; + my $res = $ua->request( POST( $url, + 'Content_Type' => 'form-data', + 'Content' => [ 'username' => $username, + 'pass' => $password, + 'custid' => $username, + 'clicode' => $clicode, + 'file1' => [ $zipfile ], + ], + ) + ); + + die "upload failed: ". $res->status_line. "\n" + unless $res->is_success; + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + +1; diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily index 119f93a59..728fa969a 100755 --- a/FS/bin/freeside-daily +++ b/FS/bin/freeside-daily @@ -15,6 +15,10 @@ adminsuidsetup $user; use FS::Cron::bill qw(bill); bill(%opt); +#you can skip this just by not having the config +use FS::Cron::upload qw(upload); +upload(%opt); + # Send alerts about upcoming credit card expiration. use FS::Cron::alert_expiration qw(alert_expiration); my $conf = new FS::Conf; diff --git a/FS/bin/freeside-monthly b/FS/bin/freeside-monthly index 1e41b780e..a81e3e9ed 100755 --- a/FS/bin/freeside-monthly +++ b/FS/bin/freeside-monthly @@ -15,6 +15,9 @@ adminsuidsetup $user; use FS::Cron::bill qw(bill); bill(%opt, 'check_freq'=>'1m' ); +use FS::Cron::upload qw(upload); +upload(%opt); + ### # subroutines ### |