[freeside-commits]
freeside/FS/FS AccessRight.pm, 1.13, 1.14 Conf.pm,
1.170, 1.171 cust_main.pm, 1.252, 1.253 Schema.pm, 1.34,
1.35 svc_acct.pm, 1.206, 1.207
Jeff Finucane,420,,
jeff at wavetail.420.am
Wed Nov 29 18:27:57 PST 2006
Update of /home/cvs/cvsroot/freeside/FS/FS
In directory wavetail:/tmp/cvs-serv12489/FS/FS
Modified Files:
AccessRight.pm Conf.pm cust_main.pm Schema.pm svc_acct.pm
Log Message:
prepaid download/upload tracking
Index: Schema.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/Schema.pm,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- Schema.pm 24 Oct 2006 18:26:37 -0000 1.34
+++ Schema.pm 30 Nov 2006 02:27:54 -0000 1.35
@@ -837,6 +837,13 @@
'quota', 'varchar', 'NULL', $char_d, '', '',
'slipip', 'varchar', 'NULL', 15, '', '', #four TINYINTs, bah.
'seconds', 'int', 'NULL', '', '', '', #uhhhh
+ 'seconds_threshold', 'int', 'NULL', '', '', '',
+ 'upbytes', 'int', 'NULL', '', '', '',
+ 'upbytes_threshold', 'int', 'NULL', '', '', '',
+ 'downbytes', 'int', 'NULL', '', '', '',
+ 'downbytes_threshold', 'int', 'NULL', '', '', '',
+ 'totalbytes','int', 'NULL', '', '', '',
+ 'totalbytes_threshold', 'int', 'NULL', '', '', '',
'domsvc', 'int', '', '', '', '',
],
'primary_key' => 'svcnum',
@@ -923,6 +930,8 @@
'identifier', 'varchar', '', $char_d, '', '',
'amount', @money_type, '', '',
'seconds', 'int', 'NULL', '', '', '',
+ 'upbytes', 'int', 'NULL', '', '', '',
+ 'downbytes', 'int', 'NULL', '', '', '',
'agentnum', 'int', 'NULL', '', '', '',
],
'primary_key' => 'prepaynum',
Index: Conf.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/Conf.pm,v
retrieving revision 1.170
retrieving revision 1.171
diff -u -d -r1.170 -r1.171
--- Conf.pm 14 Nov 2006 02:54:06 -0000 1.170
+++ Conf.pm 30 Nov 2006 02:27:54 -0000 1.171
@@ -1419,6 +1419,42 @@
},
{
+ 'key' => 'warning_email',
+ 'section' => '',
+ 'description' => 'Template file for warning email. Warning emails are sent to the customer email invoice destination(s) each time a svc_acct record has its usage drop below a threshold or 0. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code> <li><code>$column</code> <li><code>$amount</code> <li><code>$threshold</code></ul>',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'warning_email-from',
+ 'section' => '',
+ 'description' => 'From: address header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-cc',
+ 'section' => '',
+ 'description' => 'Additional recipient(s) (comma separated) for warning email when remaining usage reaches zero.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-subject',
+ 'section' => '',
+ 'description' => 'Subject: header for warning email',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'warning_email-mimetype',
+ 'section' => '',
+ 'description' => 'MIME type for warning email',
+ 'type' => 'select',
+ 'select_enum' => [ 'text/plain', 'text/html' ],
+ },
+
+ {
'key' => 'payby',
'section' => 'billing',
'description' => 'Available payment types.',
@@ -1715,18 +1751,25 @@
{
'key' => 'svc_acct-usage_suspend',
'section' => 'billing',
- 'description' => 'Suspends the package an account belongs to when svc_acct.seconds is decremented to 0 or below (accounts with an empty seconds value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'description' => 'Suspends the package an account belongs to when svc_acct.seconds or a bytecount is decremented to 0 or below (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
'type' => 'checkbox',
},
{
'key' => 'svc_acct-usage_unsuspend',
'section' => 'billing',
- 'description' => 'Unuspends the package an account belongs to when svc_acct.seconds is incremented from 0 or below to a positive value (accounts with an empty seconds value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'description' => 'Unuspends the package an account belongs to when svc_acct.seconds or a bytecount is incremented from 0 or below to a positive value (accounts with an empty seconds and up|down|totalbytes value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
'type' => 'checkbox',
},
{
+ 'key' => 'svc_acct-usage_threshold',
+ 'section' => 'billing',
+ 'description' => 'The threshold (expressed as percentage) of acct.seconds or acct.up|down|totalbytes at which a warning message is sent to a service holder. Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd. Defaults to 80.',
+ 'type' => 'text',
+ },
+
+ {
'key' => 'cust-fields',
'section' => 'UI',
'description' => 'Which customer fields to display on reports by default',
Index: svc_acct.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/svc_acct.pm,v
retrieving revision 1.206
retrieving revision 1.207
diff -u -d -r1.206 -r1.207
--- svc_acct.pm 27 Nov 2006 01:08:47 -0000 1.206
+++ svc_acct.pm 30 Nov 2006 02:27:54 -0000 1.207
@@ -9,6 +9,8 @@
$username_uppercase $username_percent
$password_noampersand $password_noexclamation
$welcome_template $welcome_from $welcome_subject $welcome_mimetype
+ $warning_template $warning_from $warning_subject $warning_mimetype
+ $warning_cc
$smtpmachine
$radius_password $radius_ip
$dirhash
@@ -76,6 +78,22 @@
$welcome_subject = '';
$welcome_mimetype = '';
}
+ if ( $conf->exists('warning_email') ) {
+ $warning_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('warning_email') ]
+ ) or warn "can't create warning email template: $Text::Template::ERROR";
+ $warning_from = $conf->config('warning_email-from'); # || 'your-isp-is-dum'
+ $warning_subject = $conf->config('warning_email-subject') || 'Warning';
+ $warning_mimetype = $conf->config('warning_email-mimetype') || 'text/plain';
+ $warning_cc = $conf->config('warning_email-cc');
+ } else {
+ $warning_template = '';
+ $warning_from = '';
+ $warning_subject = '';
+ $warning_mimetype = '';
+ $warning_cc = '';
+ }
$smtpmachine = $conf->config('smtpmachine');
$radius_password = $conf->config('radius-password') || 'Password';
$radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address';
@@ -168,6 +186,12 @@
=item seconds -
+=item upbytes -
+
+=item downbytes -
+
+=item totalbytes -
+
=item domsvc - svcnum from svc_domain
=item radius_I<Radius_Attribute> - I<Radius-Attribute> (reply)
@@ -721,6 +745,10 @@
#|| $self->ut_number('domsvc')
|| $self->ut_foreign_key('domsvc', 'svc_domain', 'svcnum' )
|| $self->ut_textn('sec_phrase')
+ || $self->ut_snumbern('seconds')
+ || $self->ut_snumbern('upbytes')
+ || $self->ut_snumbern('downbytes')
+ || $self->ut_snumbern('totalbytes')
;
return $error if $error;
@@ -1215,6 +1243,72 @@
qsearch('acct_snarf', { 'svcnum' => $self->svcnum } );
}
+=item decrement_upbytes OCTETS
+
+Decrements the I<upbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_upbytes {
+ shift->_op_usage('-', 'upbytes', @_);
+}
+
+=item increment_upbytes OCTETS
+
+Increments the I<upbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_upbytes {
+ shift->_op_usage('+', 'upbytes', @_);
+}
+
+=item decrement_downbytes OCTETS
+
+Decrements the I<downbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_downbytes {
+ shift->_op_usage('-', 'downbytes', @_);
+}
+
+=item increment_downbytes OCTETS
+
+Increments the I<downbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_downbytes {
+ shift->_op_usage('+', 'downbytes', @_);
+}
+
+=item decrement_totalbytes OCTETS
+
+Decrements the I<totalbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub decrement_totalbytes {
+ shift->_op_usage('-', 'totalbytes', @_);
+}
+
+=item increment_totalbytes OCTETS
+
+Increments the I<totalbytes> field of this record by the given amount. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_totalbytes {
+ shift->_op_usage('+', 'totalbytes', @_);
+}
+
=item decrement_seconds SECONDS
Decrements the I<seconds> field of this record by the given amount. If there
@@ -1223,7 +1317,7 @@
=cut
sub decrement_seconds {
- shift->_op_seconds('-', @_);
+ shift->_op_usage('-', 'seconds', @_);
}
=item increment_seconds SECONDS
@@ -1234,7 +1328,7 @@
=cut
sub increment_seconds {
- shift->_op_seconds('+', @_);
+ shift->_op_usage('+', 'seconds', @_);
}
@@ -1243,20 +1337,32 @@
'+' => 'unsuspend',
);
my %op2condition = (
- '-' => sub { my($self, $seconds) = @_;
- $self->seconds - $seconds <= 0;
+ '-' => sub { my($self, $column, $amount) = @_;
+ $self->$column - $amount <= 0;
},
- '+' => sub { my($self, $seconds) = @_;
- $self->seconds + $seconds > 0;
+ '+' => sub { my($self, $column, $amount) = @_;
+ $self->$column + $amount > 0;
+ },
+);
+my %op2warncondition = (
+ '-' => sub { my($self, $column, $amount) = @_;
+ my $threshold = $column . '_threshold';
+ $self->$column - $amount <= $self->$threshold + 0;
+ },
+ '+' => sub { my($self, $column, $amount) = @_;
+ $self->$column + $amount > 0;
},
);
-sub _op_seconds {
- my( $self, $op, $seconds ) = @_;
- warn "$me _op_seconds called for svcnum ". $self->svcnum.
- ' ('. $self->email. "): $op $seconds\n"
+sub _op_usage {
+ my( $self, $op, $column, $amount ) = @_;
+
+ warn "$me _op_usage called for $column on svcnum ". $self->svcnum.
+ ' ('. $self->email. "): $op $amount\n"
if $DEBUG;
+ return '' unless $amount;
+
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -1268,24 +1374,24 @@
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $sql = "UPDATE svc_acct SET seconds = ".
- " CASE WHEN seconds IS NULL THEN 0 ELSE seconds END ". #$seconds||0
+ my $sql = "UPDATE svc_acct SET $column = ".
+ " CASE WHEN $column IS NULL THEN 0 ELSE $column END ". #$column||0
" $op ? WHERE svcnum = ?";
warn "$me $sql\n"
if $DEBUG;
my $sth = $dbh->prepare( $sql )
or die "Error preparing $sql: ". $dbh->errstr;
- my $rv = $sth->execute($seconds, $self->svcnum);
+ my $rv = $sth->execute($amount, $self->svcnum);
die "Error executing $sql: ". $sth->errstr
unless defined($rv);
- die "Can't update seconds for svcnum". $self->svcnum
+ die "Can't update $column for svcnum". $self->svcnum
if $rv == 0;
my $action = $op2action{$op};
if ( $conf->exists("svc_acct-usage_$action")
- && &{$op2condition{$op}}($self, $seconds) ) {
+ && &{$op2condition{$op}}($self, $column, $amount) ) {
#my $error = $self->$action();
my $error = $self->cust_svc->cust_pkg->$action();
if ( $error ) {
@@ -1294,6 +1400,30 @@
}
}
+ if ($warning_template && &{$op2warncondition{$op}}($self, $column, $amount)) {
+ my $wqueue = new FS::queue {
+ 'svcnum' => $self->svcnum,
+ 'job' => 'FS::svc_acct::reached_threshold',
+ };
+
+ my $to = '';
+ if ($op eq '-'){
+ $to = $warning_cc if &{$op2condition{$op}}($self, $column, $amount);
+ }
+
+ # x_threshold race
+ my $error = $wqueue->insert(
+ 'svcnum' => $self->svcnum,
+ 'op' => $op,
+ 'column' => $column,
+ 'to' => $to,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error queuing threshold activity: $error";
+ }
+ }
+
warn "$me update successful; committing\n"
if $DEBUG;
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -1302,6 +1432,20 @@
}
+=item is_rechargeable
+
+Returns true if this svc_account can be "rechaged" and false otherwise.
+
+=cut
+
+sub is_rechargable {
+ my $self = shift;
+ $self->seconds ne ''
+ || $self->upbytes ne ''
+ || $self->downbytes ne ''
+ || $self->totalbytes ne '';
+}
+
=item seconds_since TIMESTAMP
Returns the number of seconds this account has been online since TIMESTAMP,
@@ -1785,6 +1929,82 @@
$html;
}
+=item reached_threshold
+
+Performs some activities when svc_acct thresholds (such as number of seconds
+remaining) are reached.
+
+=cut
+
+sub reached_threshold {
+ my %opt = @_;
+
+ my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } );
+ die "Cannot find svc_acct with svcnum " . $opt{'svcnum'} unless $svc_acct;
+
+ if ( $opt{'op'} eq '+' ){
+ $svc_acct->setfield( $opt{'column'}.'_threshold',
+ int($svc_acct->getfield($opt{'column'})
+ * ( $conf->exists('svc_acct-usage_threshold')
+ ? $conf->config('svc_acct-usage_threshold')/100
+ : 0.80
+ )
+ )
+ );
+ my $error = $svc_acct->replace;
+ die $error if $error;
+ }elsif ( $opt{'op'} eq '-' ){
+
+ my $threshold = $svc_acct->getfield( $opt{'column'}.'_threshold' );
+ return '' if ($threshold eq '' && opt{'column'} eq 'totalbytes');
+
+ $svc_acct->setfield( $opt{'column'}.'_threshold', 0 );
+ my $error = $svc_acct->replace;
+ die $error if $error; # email next time, i guess
+
+ if ( $warning_template ) {
+ eval "use FS::Misc qw(send_email)";
+ die $@ if $@;
+
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ my $cust_main = $cust_pkg->cust_main;
+
+ my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ }
+ $cust_main->invoicing_list,
+ $svc_acct->email,
+ ($opt{'to'} ? $opt{'to'} : ())
+ );
+
+ my $mimetype = $warning_mimetype;
+ $mimetype .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
+
+ my $body = $warning_template->fill_in( HASH => {
+ 'custnum' => $cust_main->custnum,
+ 'username' => $svc_acct->username,
+ 'password' => $svc_acct->_password,
+ 'first' => $cust_main->first,
+ 'last' => $cust_main->getfield('last'),
+ 'pkg' => $cust_pkg->part_pkg->pkg,
+ 'column' => $opt{'column'},
+ 'amount' => $svc_acct->getfield($opt{'column'}),
+ 'threshold' => $threshold,
+ } );
+
+
+ my $error = send_email(
+ 'from' => $warning_from,
+ 'to' => $to,
+ 'subject' => $warning_subject,
+ 'content-type' => $mimetype,
+ 'body' => [ map "$_\n", split("\n", $body) ],
+ );
+ die $error if $error;
+ }
+ }else{
+ die "unknown op: " . $opt{'op'};
+ }
+}
+
=back
=head1 BUGS
Index: AccessRight.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/AccessRight.pm,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- AccessRight.pm 27 Oct 2006 17:01:29 -0000 1.13
+++ AccessRight.pm 30 Nov 2006 02:27:54 -0000 1.14
@@ -115,6 +115,7 @@
# customer service rights
###
'Provision customer service',
+ 'Recharge customer service',
'Unprovision customer service',
'View/link unlinked services', #not agent-virtualizable without more work
Index: cust_main.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/cust_main.pm,v
retrieving revision 1.252
retrieving revision 1.253
diff -u -d -r1.252 -r1.253
--- cust_main.pm 5 Nov 2006 19:34:09 -0000 1.252
+++ cust_main.pm 30 Nov 2006 02:27:54 -0000 1.253
@@ -692,21 +692,23 @@
''; #no error
}
-=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF ]
+=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF, UPBYTEREF, DOWNBYTEREF ]
Recharges this (existing) customer with the specified prepaid card (see
L<FS::prepay_credit>), specified either by I<identifier> or as an
FS::prepay_credit object. If there is an error, returns the error, otherwise
returns false.
-Optionally, two scalar references can be passed as well. They will have their
-values filled in with the amount and number of seconds applied by this prepaid
+Optionally, four scalar references can be passed as well. They will have their
+values filled in with the amount, number of seconds, and number of upload and
+download bytes applied by this prepaid
card.
=cut
sub recharge_prepay {
- my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+ my( $self, $prepay_credit, $amountref, $secondsref,
+ $upbytesref, $downbytesref ) = @_;
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
@@ -719,10 +721,14 @@
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my( $amount, $seconds ) = ( 0, 0 );
+ my( $amount, $seconds, $upbytes, $downbytes ) = ( 0, 0, 0, 0 );
- my $error = $self->get_prepay($prepay_credit, \$amount, \$seconds)
+ my $error = $self->get_prepay($prepay_credit, \$amount,
+ \$seconds, \$upbytes, \$downbytes)
|| $self->increment_seconds($seconds)
+ || $self->increment_upbytes($upbytes)
+ || $self->increment_downbytes($downbytes)
+ || $self->increment_totalbytes($upbytes + $downbytes)
|| $self->insert_cust_pay_prepay( $amount,
ref($prepay_credit)
? $prepay_credit->identifier
@@ -736,6 +742,8 @@
if ( defined($amountref) ) { $$amountref = $amount; }
if ( defined($secondsref) ) { $$secondsref = $seconds; }
+ if ( defined($upbytesref) ) { $$upbytesref = $upbytes; }
+ if ( defined($downbytesref) ) { $$downbytesref = $downbytes; }
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -759,7 +767,7 @@
sub get_prepay {
- my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+ my( $self, $prepay_credit, $amountref, $secondsref, $upref, $downref) = @_;
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
@@ -806,12 +814,50 @@
$$amountref += $prepay_credit->amount;
$$secondsref += $prepay_credit->seconds;
+ $$upref += $prepay_credit->upbytes;
+ $$downref += $prepay_credit->downbytes;
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
+=item increment_upbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of upbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_upbytes {
+ _increment_column( shift, 'upbytes', @_);
+}
+
+=item increment_downbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of downbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_downbytes {
+ _increment_column( shift, 'downbytes', @_);
+}
+
+=item increment_totalbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of totalbytes. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_totalbytes {
+ _increment_column( shift, 'totalbytes', @_);
+}
+
=item increment_seconds SECONDS
Updates this customer's single or primary account (see L<FS::svc_acct>) by
@@ -821,10 +867,24 @@
=cut
sub increment_seconds {
- my( $self, $seconds ) = @_;
- warn "$me increment_seconds called: $seconds seconds\n"
+ _increment_column( shift, 'seconds', @_);
+}
+
+=item _increment_column AMOUNT
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of seconds or bytes. If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub _increment_column {
+ my( $self, $column, $amount ) = @_;
+ warn "$me increment_column called: $column, $amount\n"
if $DEBUG;
+ return '' unless $amount;
+
my @cust_pkg = grep { $_->part_pkg->svcpart('svc_acct') }
$self->ncancelled_pkgs;
@@ -854,7 +914,8 @@
' ('. $svc_acct->email. ")\n"
if $DEBUG > 1;
- $svc_acct->increment_seconds($seconds);
+ $column = "increment_$column";
+ $svc_acct->$column($amount);
}
More information about the freeside-commits
mailing list