use Date::Format;
use Time::Duration;
use Time::Local qw(timelocal_nocheck);
-use Business::CreditCard;
+use Business::CreditCard 0.35;
use HTML::Entities;
use Text::CSV_XS;
use Spreadsheet::WriteExcel;
};
}
+sub customer_recurring {
+ my $p = shift;
+
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ my %return;
+
+ my $conf = new FS::Conf;
+
+ my $search = { 'custnum' => $custnum };
+ $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
+ my $cust_main = qsearchs('cust_main', $search )
+ or return { 'error' => "customer_info_short: unknown custnum $custnum" };
+
+ $return{'display_recurring'} = [ $cust_main->display_recurring ];
+
+ return { 'error' => '',
+ 'custnum' => $custnum,
+ %return,
+ };
+}
+
sub billing_history {
my $p = shift;
# @svc_x;
my @svcs; # stuff to return to the client
+ my %bytes_used_total; # for _used columns only
foreach my $cust_svc (@cust_svc) {
my $svc_x = $cust_svc->svc_x;
my($label, $value) = $cust_svc->label;
# would it make sense to put this in a svc_* method?
+ if (!$hide_usage and grep(/^$svcdb$/, qw(svc_acct svc_broadband)) and $part_svc->part_export_usage) {
+ my $last_bill = $cust_pkg->last_bill || 0;
+ my $now = time;
+ my $up_used = $cust_svc->attribute_since_sqlradacct($last_bill,$now,'AcctInputOctets');
+ my $down_used = $cust_svc->attribute_since_sqlradacct($last_bill,$now,'AcctOutputOctets');
+ %hash = (
+ %hash,
+ 'seconds_used' => $cust_svc->seconds_since_sqlradacct($last_bill,$now),
+ 'upbytes_used' => display_bytecount($up_used),
+ 'downbytes_used' => display_bytecount($down_used),
+ 'totalbytes_used' => display_bytecount($up_used + $down_used)
+ );
+ $bytes_used_total{'seconds_used'} += $hash{'seconds_used'};
+ $bytes_used_total{'upbytes_used'} += $up_used;
+ $bytes_used_total{'downbytes_used'} += $down_used;
+ $bytes_used_total{'totalbytes_used'} += $up_used + $down_used;
+ }
+
if ( $svcdb eq 'svc_acct' ) {
foreach (qw(username email finger seconds)) {
$hash{$_} = $svc_x->$_;
push @svcs, \%hash;
} # foreach $cust_svc
+ foreach my $field (keys %bytes_used_total) {
+ if ($field =~ /bytes/) {
+ $bytes_used_total{$field} = display_bytecount($bytes_used_total{$field});
+ }
+ }
+
return {
'svcnum' => $session->{'svcnum'},
'custnum' => $custnum,
'date_format' => $conf->config('date_format') || '%m/%d/%Y',
'view_usage_nodomain' => $conf->exists('selfservice-view_usage_nodomain'),
'svcs' => \@svcs,
+ 'bytes_used_total' => \%bytes_used_total,
'usage_pools' => [
map { $usage_pools{$_} }
sort { $a cmp $b }
my $conf = new FS::Conf;
if ( $conf->exists('signup_server-realtime') ) {
- my $bill_error = _do_bop_realtime( $cust_main, $status );
+ my $bill_error = _do_bop_realtime( $cust_main, $status, 'collect'=>$p->{run_bill_events} );
if ($bill_error) {
$cust_pkg->cancel('quiet'=>1);
sub _do_bop_realtime {
my ($cust_main, $status, %opt) = @_;
- my $old_balance = $cust_main->balance;
-
- my @cust_bill;
- my $bill_error = $cust_main->bill(
- 'return_bill' => \@cust_bill,
- );
+ my $old_balance = $cust_main->balance;
- $bill_error ||= $cust_main->apply_payments_and_credits;
+ my @cust_bill;
+ my $bill_error = $cust_main->bill(
+ 'return_bill' => \@cust_bill,
+ );
- $bill_error ||= $cust_main->realtime_collect('selfservice' => 1)
- if $cust_main->payby =~ /^(CARD|CHEK)$/;
+ $bill_error ||= $cust_main->apply_payments_and_credits;
- if ( $cust_main->balance > $old_balance
- && $cust_main->balance > 0
- && ( $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/
- || $status eq 'suspended'
- )
- )
- {
- unless ( $opt{'no_invoice_void'} ) {
+ $bill_error ||= $cust_main->realtime_collect('selfservice' => 1)
+ if $cust_main->payby =~ /^(CARD|CHEK)$/;
- #this used to apply a credit, but now we can void invoices...
- foreach my $cust_bill (@cust_bill) {
- my $voiderror = $cust_bill->void('automatic payment failed');
- warn "Error voiding cust bill after decline: $voiderror" if $voiderror;
- }
+ if ( $cust_main->balance > $old_balance
+ && $cust_main->balance > 0
+ && ( $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/
+ || $status eq 'suspended'
+ )
+ )
+ {
+ unless ( $opt{'no_invoice_void'} ) {
+ #this used to apply a credit, but now we can void invoices...
+ foreach my $cust_bill (@cust_bill) {
+ my $voiderror = $cust_bill->void('automatic payment failed');
+ warn "Error voiding cust bill after decline: $voiderror" if $voiderror;
}
- return { 'error' => '_decline', 'bill_error' => $bill_error };
}
- '';
+ return { 'error' => '_decline', 'bill_error' => $bill_error };
+ }
+
+ if ( $opt{'collect'} ) {
+ my $collect_error = $cust_main->collect();
+ return { 'error' => '_decline', 'bill_error' => $collect_error }
+ if $collect_error; #?
+ }
+
+ '';
}
sub renew_info {
or return { 'error' => "Can't resume session" }; #better error message
my $custnum = $session->{'custnum'};
-
my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
or return { 'error' => "unknown custnum $custnum" };
my $pkgnum = $p->{'pkgnum'};
-
my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
'pkgnum' => $pkgnum, } )
or return { 'error' => "unknown pkgnum $pkgnum" };
- my $error = $cust_pkg->cancel('quiet' => 1);
+ my $error = $cust_pkg->cancel( 'quiet' => 1,
+ 'date' => $p->{'date'},
+ );
return { 'error' => $error };
-
}
sub provision_phone {
)
&& ! $svc_acct->check_password($p->{'old_password'});
- # should move password length checks into is_password_allowed
- $error = 'Password too short.'
- if length($p->{'new_password'}) < ($conf->config('passwordmin') || 6);
- $error = 'Password too long.'
- if length($p->{'new_password'}) > ($conf->config('passwordmax') || 8);
-
$error ||= $svc_acct->is_password_allowed($p->{'new_password'})
|| $svc_acct->set_password($p->{'new_password'})
|| $svc_acct->replace();
}
+sub validate_passwd {
+ my $p = shift;
+
+ my %result;
+ %result = ( 'fieldid' => $p->{'fieldid'} )
+ if $p->{'fieldid'} =~ /^\w+$/;
+
+ return { %result, 'password_invalid' => 'Enter new password' }
+ unless length($p->{'check_password'});
+
+ my $svc_acct;
+ if ($p->{'svcnum'}) {
+ # false laziness with myaccount_passwd
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { %result, 'error' => $session } if $context eq 'error';
+
+ $custnum =~ /^(\d+)$/ or die "illegal custnum";
+ my $search = " AND custnum = $1";
+ $search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
+
+ $svc_acct = qsearchs( {
+ 'table' => 'svc_acct',
+ 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum ) '.
+ 'LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ 'LEFT JOIN cust_main USING ( custnum ) ',
+ 'hashref' => { 'svcnum' => $p->{'svcnum'}, },
+ 'extra_sql' => $search, #important
+ } )
+ or return { %result, 'error' => "Service not found" };
+ # end false laziness
+ }
+
+ unless ($svc_acct) {
+ my $conf = new FS::Conf;
+ my $agentnum = $p->{'agentnum'};
+ return { %result, 'password_valid' => 1 }
+ if $conf->config_bool('password-insecure', $p->{'agentnum'});
+ }
+
+ $svc_acct ||= new FS::svc_acct {};
+
+ my $error = $svc_acct->is_password_allowed($p->{'check_password'});
+ return { %result, 'password_invalid' => $error } if $error;
+ return { %result, 'password_valid' => 1 };
+}
+
sub list_tickets {
my $p = shift;
my($context, $session, $custnum) = _custoragent_session_custnum($p);