$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
$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);
}
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;
}
}
-=item config_binary KEY [ AGENTNUM ]
+=item config_binary KEY [ AGENTNUM [ NODEFAULT ] ]
Returns the exact scalar value for key.
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.
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
'type' => 'textarea',
},
+ {
+ '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',
--- /dev/null
+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;
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;
use FS::Cron::bill qw(bill);
bill(%opt, 'check_freq'=>'1m' );
+use FS::Cron::upload qw(upload);
+upload(%opt);
+
###
# subroutines
###
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;\
+++ /dev/null
-#!/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
% @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;
)
%>: <% $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>)
% }
</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"
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 );