'totalbytes','bigint', 'NULL', '', '', '',
'totalbytes_threshold', 'bigint', 'NULL', '', '', '',
'domsvc', 'int', '', '', '', '',
+ 'last_login', @date_type, '', '',
+ 'last_logout', @date_type, '', '',
],
'primary_key' => 'svcnum',
#'unique' => [ [ 'username', 'domsvc' ] ],
my $conf = new FS::Conf;
+ my $fdbh = dbh;
my $dbh = sqlradius_connect( map $self->option($_),
qw( datasrc username password ) );
+ my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
my @fields = qw( radacctid username realm acctsessiontime );
my @param = ();
my $sth = $dbh->prepare("
SELECT RadAcctId, UserName, Realm, AcctSessionTime,
+ $str2time AcctStartTime), $str2time AcctStopTime),
AcctInputOctets, AcctOutputOctets
FROM radacct
WHERE FreesideStatus IS NULL
$sth->execute() or die $sth->errstr;
while ( my $row = $sth->fetchrow_arrayref ) {
- my($RadAcctId, $UserName, $Realm, $AcctSessionTime,
- $AcctInputOctets, $AcctOutputOctets) = @$row;
+ my($RadAcctId, $UserName, $Realm, $AcctSessionTime, $AcctStartTime,
+ $AcctStopTime, $AcctInputOctets, $AcctOutputOctets) = @$row;
warn "processing record: ".
"$RadAcctId ($UserName\@$Realm for ${AcctSessionTime}s"
if $DEBUG;
WHERE svc_domain.svcnum = svc_acct.domsvc ) ";
}
+ my $oldAutoCommit = $FS::UID::AutoCommit; # can't undo side effects, but at
+ local $FS::UID::AutoCommit = 0; # least we can avoid over counting
+
my @svc_acct =
grep { qsearch( 'export_svc', { 'exportnum' => $self->exportnum,
'svcpart' => $_->cust_svc->svcpart, } )
warn "WARNING: multiple svc_acct records found $errinfo - skipping\n";
} else {
warn "found svc_acct ". $svc_acct[0]->svcnum. " $errinfo\n" if $DEBUG;
- _try_decrement($svc_acct[0], 'seconds', $AcctSessionTime)
- and $status='done';
- _try_decrement($svc_acct[0], 'upbytes', $AcctInputOctets)
- and $status='done';
- _try_decrement($svc_acct[0], 'downbytes', $AcctOutputOctets)
- and $status='done';
- _try_decrement($svc_acct[0], 'totalbytes', $AcctInputOctets +
- $AcctOutputOctets)
- and $status='done';
+ $svc_acct[0]->last_login($AcctStartTime);
+ $svc_acct[0]->last_logout($AcctStopTime);
+ my @stati;
+ push @stati, _try_decrement($svc_acct[0], 'seconds', $AcctSessionTime);
+ push @stati, _try_decrement($svc_acct[0], 'upbytes', $AcctInputOctets);
+ push @stati, _try_decrement($svc_acct[0], 'downbytes', $AcctOutputOctets);
+ push @stati, _try_decrement($svc_acct[0], 'totalbytes', $AcctInputOctets +
+ $AcctOutputOctets);
+ $status=join(' ', @stati);
}
warn "setting FreesideStatus to $status $errinfo\n" if $DEBUG;
) or die $dbh->errstr;
$psth->execute($status, $RadAcctId) or die $psth->errstr;
+ $fdbh->commit or die $fdbh->errstr if $oldAutoCommit;
+
}
}
} else {
warn " no existing $column value for svc_acct - skipping\n" if $DEBUG;
}
- return '';
+ return 'skipped';
}
1;
{
'name' => 'Account',
'longname_plural' => 'Access accounts and mailboxes',
- 'sorts' => [ 'username', 'uid', ],
+ 'sorts' => [ 'username', 'uid', 'last_login', ],
'display_weight' => 10,
'cancel_weight' => 50,
'fields' => {
'format' => \&FS::UI::bytecount::display_bytecount,
'parse' => \&FS::UI::bytecount::parse_bytecount,
},
+ 'last_login'=> {
+ label => 'Last login',
+ type => 'disabled',
+ },
+ 'last_logout'=> {
+ label => 'Last logout',
+ type => 'disabled',
+ },
},
};
}
};
}
+sub last_login {
+ shift->_lastlog('in', @_);
+}
+
+sub last_logout {
+ shift->_lastlog('out', @_);
+}
+
+sub _lastlog {
+ my( $self, $op, $time ) = @_;
+
+ if ( defined($time) ) {
+ warn "$me last_log$op called on svcnum ". $self->svcnum.
+ ' ('. $self->email. "): $time\n"
+ if $DEBUG;
+
+ 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 $sql = "UPDATE svc_acct SET last_log$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($time, $self->svcnum);
+ die "Error executing $sql: ". $sth->errstr
+ unless defined($rv);
+ die "Can't update last_log$op for svcnum". $self->svcnum
+ if $rv == 0;
+
+ warn "$me update successful; committing\n"
+ if $DEBUG;
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ $self->{'Hash'}->{"last_log$op"} = $time;
+ }else{
+ $self->getfield("last_log$op");
+ }
+}
+
=item search_sql STRING
Class method which returns an SQL fragment to search for the given string.
$self->cust_svc->get_session_history(@_);
}
+=item last_login_text
+
+Returns text describing the time of last login.
+
+=cut
+
+sub last_login_text {
+ my $self = shift;
+ $self->last_login ? ctime($self->last_login) : 'unknown';
+}
+
=item get_cdrs TIMESTAMP_START TIMESTAMP_END [ 'OPTION' => 'VALUE ... ]
=cut
'Service',
'Account',
'UID',
+ 'Last Login',
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
'svc',
'email',
'uid',
+ 'last_login_text',
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
$link,
$link,
+ $link,
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rlll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllr'. FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
'',
+ '',
FS::UI::Web::cust_colors(),
],
'style' => [
'',
'',
'',
+ '',
FS::UI::Web::cust_styles(),
],
)
$sortby = "LOWER($sortby)"
if $sortby eq 'username';
push @extra_sql, "$sortby IS NOT NULL"
- if $sortby eq 'uid';
+ if ($sortby eq 'uid' or $sortby eq 'last_login');
$orderby = "ORDER BY $sortby";
}
Upload: <B><% sprintf("%.3f", $input) %></B> megabytes<BR>
Download: <B><% sprintf("%.3f", $output) %></B> megabytes<BR>
+ Last Login: <B><% $svc_acct->last_login_text %></B><BR>
% my $href = qq!<A HREF="${p}search/sqlradius.cgi?svcnum=$svcnum!;
View session detail: