summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjeff <jeff>2009-08-12 14:58:50 +0000
committerjeff <jeff>2009-08-12 14:58:50 +0000
commit56a2965996454a0649d43ecbc062beda61106e21 (patch)
tree12c4c2167844a44b09ff5da4cf2bf128cdcf2bc7
parent600a15aa56c212e1206fced9cbcf9de103c2f95b (diff)
internalize billco-upload and automate the transfer to the provider RT#5902
-rw-r--r--FS/FS/Conf.pm72
-rw-r--r--FS/FS/Cron/upload.pm176
-rwxr-xr-xFS/bin/freeside-daily4
-rwxr-xr-xFS/bin/freeside-monthly3
-rw-r--r--Makefile1
-rw-r--r--bin/billco-upload40
-rw-r--r--httemplate/config/config-view.cgi22
7 files changed, 246 insertions, 72 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
###
diff --git a/Makefile b/Makefile
index 35d904496..79135e7a7 100644
--- a/Makefile
+++ b/Makefile
@@ -200,6 +200,7 @@ perl-modules:
perl -p -i -e "\
s/%%%SELFSERVICE_USER%%%/${SELFSERVICE_USER}/g;\
s/%%%SELFSERVICE_MACHINES%%%/${SELFSERVICE_MACHINES}/g;\
+ s|%%%FREESIDE_EXPORT%%%|${FREESIDE_EXPORT}|g;\
" blib/lib/FS/Cron/*.pm;\
perl -p -i -e "\
s|%%%FREESIDE_EXPORT%%%|${FREESIDE_EXPORT}|g;\
diff --git a/bin/billco-upload b/bin/billco-upload
deleted file mode 100644
index 3a02ec8bb..000000000
--- a/bin/billco-upload
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-AGENTNUMS="1 2 3 5 8 9 10"
-
-date=`date +"%Y%m%d"`
-dir="/usr/local/etc/freeside/export.DBI:Pg:dbname=freeside/cust_bill"
-lock=".billco-upload.lock"
-cd "$dir"
-
-failed_mutex()
-{
- echo "billco-upload already running; exiting"
- exit 1
-}
-
-#acquire mutex
-[ -f $lock ] && {
- failed_mutex
-} || {
- echo $$ > $lock
- [ $(cat $lock 2>/dev/null) -eq $$ ] || failed_mutex
-}
-
-for AGENTNUM in $AGENTNUMS; do
-
- for a in header detail; do
- mv agentnum$AGENTNUM-$a.csv agentnum$AGENTNUM-$date-$a.csv
- done
-
- zip agentnum$AGENTNUM-$date.zip agentnum$AGENTNUM-$date-header.csv agentnum$AGENTNUM-$date-detail.csv
-
-# Remove if trying to find problems with billco upload files
- rm *$AGENTNUM-$date*.csv
-
- echo $dir/agentnum$AGENTNUM-$date.zip
-
-done
-
-#release mutex
-rm -f $lock
diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi
index 9e9e64eb2..0f6c99232 100644
--- a/httemplate/config/config-view.cgi
+++ b/httemplate/config/config-view.cgi
@@ -66,7 +66,7 @@ Click on a configuration value to change it.
% @agents = ( '' );
% if ( $i->per_agent ) {
% foreach my $agent (@all_agents) {
-% if ( defined(_config_agentonly($conf, $i->key, $agent->agentnum)) ) {
+% if ( defined($conf->conf( $i->key, $agent->agentnum, 1 ) ) ) {
% push @agents, $agent;
% } else {
% push @add_agents, $agent;
@@ -99,15 +99,14 @@ Click on a configuration value to change it.
)
%>: <% $i->description %>
% if ( $agent && $cgi->param('showagent') ) {
-% my $confnum =
-% _config_agentonly($conf, $i->key, $agent->agentnum)->confnum;
+% my $confnum = $conf->conf( $i->key, $agent->agentnum, 1 )->confnum;
(<A HREF="javascript:areyousure('delete this agent override', 'config-delete.cgi?confnum=<% $confnum %>;redirect=config_view_showagent')">delete agent override</A>)
% } elsif ( $i->base_key
% || ( $deleteable{$i->key} && $conf->exists($i->key) ) ) {
% my $confnum =
% $agent
-% ? _config_agentonly($conf, $i->key, $agent->agentnum)->confnum
-% : $conf->_config( $i->key )->confnum;
+% ? $conf->conf( $i->key, $agent->agentnum, 1 )->confnum
+% : $conf->conf( $i->key )->confnum;
% my $showagent = $cgi->param('showagent') ? '_showagent' : '';
(<A HREF="javascript:areyousure('delete this configuration item', 'config-delete.cgi?confnum=<% $confnum %>;redirect=config_view<%$showagent%>')">delete configuration item</A>)
% }
@@ -286,18 +285,6 @@ Click on a configuration value to change it.
</SCRIPT>
</body></html>
-<%once>
-
-#should probably be a Conf method. what else would need to use it?
-sub _config_agentonly {
- my($self,$name,$agentnum)=@_;
- my $hashref = { 'name' => $name };
- $hashref->{agentnum} = $agentnum;
- local $FS::Record::conf = undef; # XXX evil hack prevents recursion
- FS::Record::qsearchs('conf', $hashref);
-}
-
-</%once>
<%init>
die "access denied"
@@ -320,6 +307,7 @@ if ($cgi->param('agentnum') =~ /^(\d+)$/) {
my $conf = new FS::Conf;
my @config_items = grep { $page_agent ? $_->per_agent : 1 }
+ grep { $page_agent ? 1 : !$_->agentonly }
$conf->config_items;
my @deleteable = qw( invoice_latexreturnaddress invoice_htmlreturnaddress );