From: ivan Date: Sat, 12 Oct 2002 13:26:46 +0000 (+0000) Subject: sqlradacct_hour price plan to charge per-hour against an external radacct table X-Git-Tag: freeside_1_5_0pre1~191 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=6f8a6c416174bcf8095c959085d14ba820425aad sqlradacct_hour price plan to charge per-hour against an external radacct table --- diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 0c71435e1..b16d08137 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -475,7 +475,7 @@ sub cust_main { =item seconds_since TIMESTAMP Returns the number of seconds all accounts (see L) in this -package have been online since TIMESTAMP. +package have been online since TIMESTAMP, according to the session monitor. TIMESTAMP is specified as a UNIX timestamp; see L. Also see L and L for conversion functions. @@ -496,6 +496,43 @@ sub seconds_since { } +=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END DATASRC DB_USERNAME DB_PASSWORD + +Returns the numbers of seconds all accounts (see L) in this +package have been online between TIMESTAMP_START (inclusive) and TIMESTAMP_END +(exclusive), according to an external SQL radacct table, such as those +generated by ICRADIUS or FreeRADIUS. Sessions which started in the specified +range but are still open are counted from session start to the end of the +range. Also, sessions which end in the range but started earlier are counted +from the start of the range to session end. Finally, sessions which start +before the range but end after (or are still open) are counted for the entire +range. + +TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see +L. Also see L and L for conversion +functions. + + +=cut + +sub seconds_since_sqlradacct { + my($self, $start, $end, $datasrc, $db_user, $db_pass) = @_; + + my $dbh = DBI->connect($datasrc, $db_user, $db_pass) + or die "can't connect to $datasrc: ". $DBI::errstr; + + my $seconds = 0; + + foreach my $cust_svc ( + grep { $_->part_svc->svcdb eq 'svc_acct' } $self->cust_svc + ) { + $seconds += $cust_svc->seconds_since_sqlradacct($start, $end, $dbh); + } + + $seconds; + +} + =back =head1 SUBROUTINES @@ -678,7 +715,7 @@ sub order { =head1 VERSION -$Id: cust_pkg.pm,v 1.24 2002-09-17 09:19:06 ivan Exp $ +$Id: cust_pkg.pm,v 1.25 2002-10-12 13:26:45 ivan Exp $ =head1 BUGS diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm index 9d510b38a..4fc663450 100644 --- a/FS/FS/cust_svc.pm +++ b/FS/FS/cust_svc.pm @@ -336,11 +336,93 @@ sub seconds_since { $sth->fetchrow_arrayref->[0]; } +=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ( DBI_DATABASE_HANDLE | DATASRC DB_USERNAME DB_PASSWORD ) + +See L. Equivalent to +$cust_svc->svc_x->seconds_since, but more efficient. Meaningless for records +where B is not "svc_acct". + +NOTE: specifying a DATASRC/USERNAME/PASSWORD instead of a DBI database handle +is not yet implemented. + +=cut + +#note: implementation here, POD in FS::svc_acct +sub seconds_since_sqlradacct { + my($self, $start, $end, $dbh) = @_; + + my $username = $self->svc_x->username; + + #select a unix time conversion function based on database type + my $str2time; + if ( $dbh->{Driver}->{Name} eq 'mysql' ) { + $str2time = 'UNIX_TIMESTAMP('; + } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) { + $str2time = 'EXTRACT( EPOCH FROM '; + } else { + warn "warning: unknown database type ". $dbh->{Driver}->{Name}. + "; guessing how to convert to UNIX timestamps"; + $str2time = 'extract(epoch from '; + } + + #find sessions completely within the given range + my $sth = $dbh->prepare("SELECT SUM(acctsessiontime) + FROM radacct + WHERE UserName = ? + AND $str2time AcctStartTime) >= ? + AND $str2time AcctStopTime ) < ? + AND AcctStopTime =! 0 + AND AcctStopTime IS NOT NULL" + ) or die $dbh->errstr; + $sth->execute($username, $start, $end) or die $sth->errstr; + my $regular = $sth->fetchrow_arrayref->[0]; + + #find open sessions which start in the range, count session start->range end + $sth = $dbh->prepare("SELECT SUM( ? - $str2time AcctStartTime ) ) + FROM radacct + WHERE UserName = ? + AND AcctStartTime >= ? + AND ( AcctStopTime = 0 + OR AcctStopTime IS NULL )" + ) or die $dbh->errstr; + $sth->execute($end, $username, $start) or die $sth->errstr; + my $start_during = $sth->fetchrow_arrayref->[0]; + + #find closed sessions which start before the range but stop during, + #count range start->session end + $sth = $dbh->prepare("SELECT SUM( $str2time AcctStopTime ) - ? ) + FROM radacct + WHERE UserName = ? + AND AcctStartTime < ? + AND AcctStopTime >= ? + AND AcctStopTime < ? + AND AcctStopTime != 0 + AND AcctStopTime IS NOT NULL" + ) or die $dbh->errstr; + $sth->execute($start, $username, $start, $start, $end ) or die $sth->errstr; + my $end_during = $sth->fetchrow_arrayref->[0]; + + #find closed or open sessions which start before the range but stop + # after, or are still open, count range start->range end + $sth = $dbh->prepare("SELECT COUNT(*) + FROM radacct + WHERE UserName = ? + AND AcctStartTime < ? + AND ( AcctStopTime >= ? + OR AcctStopTime = 0 + OR AcctStopTime IS NULL )" + ) or die $dbh->errstr; + $sth->execute($username, $start, $end ) or die $sth->errstr; + my $entire_range = ($end-$start) * $sth->fetchrow_arrayref->[0]; + + $regular + $end_during + $start_during + $entire_range; +} + =back =head1 VERSION -$Id: cust_svc.pm,v 1.17 2002-09-18 22:39:01 ivan Exp $ +$Id: cust_svc.pm,v 1.18 2002-10-12 13:26:45 ivan Exp $ =head1 BUGS @@ -352,6 +434,9 @@ pkg_svc records are not checked in general (here). Deleting this record doesn't check or delete the svc_* record associated with this record. +In seconds_since_sqlradacct, specifying a DATASRC/USERNAME/PASSWORD instead of +a DBI database handle is not yet implemented. + =head1 SEE ALSO L, L, L, L, diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index b5ade6fac..3afee7f64 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -921,8 +921,8 @@ sub email { =item seconds_since TIMESTAMP -Returns the number of seconds this account has been online since TIMESTAMP. -See L +Returns the number of seconds this account has been online since TIMESTAMP, +according to the session monitor (see L). TIMESTAMP is specified as a UNIX timestamp; see L. Also see L and L for conversion functions. @@ -935,6 +935,29 @@ sub seconds_since { $self->cust_svc->seconds_since(@_); } +=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END DATASRC DB_USERNAME DB_PASSWORD + +Returns the numbers of seconds this account has been online between +TIMESTAMP_START (inclusive) and TIMESTAMP_END (exclusive), according to an +external SQL radacct table, such as those generated by ICRADIUS or FreeRADIUS. +Sessions which started in the specified range but are still open are counted +from session start to the end of the range. Also, sessions which end in the +range but started earlier are counted from the start of the range to session +end. Finally, sessions which start before the range but end after (or are +still open) are counted for the entire range. + +TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see +L. Also see L and L for conversion +functions. + +=cut + +#note: POD here, implementation in FS::cust_svc +sub seconds_since_sqlradacct { + my $self = shift; + $self->cust_svc->seconds_since_sqlradacct(@_); +} + =item radius_groups Returns all RADIUS groups for this account (see L). diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi index 38d7358a5..ddd86765a 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -355,6 +355,37 @@ tie my %plans, 'Tie::IxHash', }, + 'sqlradacct_hour' => { + 'name' => 'Base charge plus charge per-hour from an external SQL radacct table', + 'fields' => { + 'setup_fee' => { 'name' => 'Setup fee for this package', + 'default' => 0, + }, + 'recur_flat' => { 'name' => 'Base monthly charge for this package', + 'default' => 0, + }, + 'sql_datasrc' => { 'name' => 'DBI data source', + 'default' => 'DBI:mysql:host=radius.server.name;dbname=radius', + }, + 'sql_username' => { 'name' => 'Database username', + 'default' => 'radius', + }, + 'sql_password' => { 'name' => 'Database password', + 'default' => '', + }, + 'recur_included_hours' => { 'name' => 'Hours included', + 'default' => 0, + }, + 'recur_hourly_charge' => { 'name' => 'Additional charge per hour', + 'default' => 0, + }, + }, + 'fieldorder' => [ 'setup_fee', 'recur_flat', 'recur_included_hours', 'recur_hourly_charge' ], + 'setup' => 'what.setup_fee.value', + 'recur' => '\'my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate))[0,1,2,3,4,5]; $mon+=$part_pkg->freq; until ($mon<12) { $mon-=12; $year++ }; $edate = timelocal($sec,$min,$hour,$mday,$mon,$year); my $hours = $cust_pkg->seconds_since_sqlradacct($sdate, $edate, \' + what.sql_datasrc + \', \' + what.sql_username + \', \' + what.sql_password + \' ) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; \' + what.recur_flat.value + \' + \' + what.recur_hourly_charge.value + \' * $hours;\'', + }, + + ; my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }