package FS::ClientAPI::MyAccount;
use strict;
-use vars qw($cache);
-use subs qw(_cache);
+use vars qw( $cache $DEBUG );
+use subs qw( _cache _provision );
+use Data::Dumper;
use Digest::MD5 qw(md5_hex);
use Date::Format;
use Business::CreditCard;
use Time::Duration;
-use FS::CGI qw(small_custview); #doh
+use FS::UI::Web::small_custview qw(small_custview); #less doh
use FS::UI::Web;
use FS::UI::bytecount;
use FS::Conf;
use FS::ClientAPI_SessionCache;
use FS::svc_acct;
use FS::svc_domain;
+use FS::svc_phone;
use FS::svc_external;
use FS::part_svc;
use FS::cust_main;
use FS::cust_main_county;
use FS::cust_pkg;
use FS::payby;
+use FS::acct_rt_transaction;
use HTML::Entities;
+$DEBUG = 0;
+
#false laziness with FS::cust_main
BEGIN {
eval "use Time::Local;";
ship_first ship_last ship_company ship_address1 ship_address2 ship_city
ship_state ship_zip ship_country ship_daytime ship_night ship_fax
payby payinfo payname paystart_month paystart_year payissue payip
+ ss paytype paystate stateid stateid_state
);
-use subs qw(_provision);
-
sub _cache {
$cache ||= new FS::ClientAPI_SessionCache( {
'namespace' => 'FS::ClientAPI::MyAccount',
sub login {
my $p = shift;
- my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
- or return { error => 'Domain '. $p->{'domain'}. ' not found' };
+ my $conf = new FS::Conf;
- my $svc_acct = qsearchs( 'svc_acct', { 'username' => $p->{'username'},
- 'domsvc' => $svc_domain->svcnum, }
- );
- return { error => 'User not found.' } unless $svc_acct;
+ my $svc_x = '';
+ if ( $p->{'domain'} eq 'svc_phone'
+ && $conf->exists('selfservice_server-phone_login') ) {
- my $conf = new FS::Conf;
- my $pkg_svc = $svc_acct->cust_svc->pkg_svc;
- return { error => 'Only primary user may log in.' }
- if $conf->exists('selfservice_server-primary_only')
- && ( ! $pkg_svc || $pkg_svc->primary_svc ne 'Y' );
+ my $svc_phone = qsearchs( 'svc_phone', { 'phonenum' => $p->{'username'} } );
+ return { error => 'Number not found.' } unless $svc_phone;
+
+ #XXX?
+ #my $pkg_svc = $svc_acct->cust_svc->pkg_svc;
+ #return { error => 'Only primary user may log in.' }
+ # if $conf->exists('selfservice_server-primary_only')
+ # && ( ! $pkg_svc || $pkg_svc->primary_svc ne 'Y' );
+
+ return { error => 'Incorrect PIN.' }
+ unless $svc_phone->check_pin($p->{'password'});
+
+ $svc_x = $svc_phone;
+
+ } else {
+
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
+ or return { error => 'Domain '. $p->{'domain'}. ' not found' };
- return { error => 'Incorrect password.' }
- unless $svc_acct->check_password($p->{'password'});
+ my $svc_acct = qsearchs( 'svc_acct', { 'username' => $p->{'username'},
+ 'domsvc' => $svc_domain->svcnum, }
+ );
+ return { error => 'User not found.' } unless $svc_acct;
+
+ #my $pkg_svc = $svc_acct->cust_svc->pkg_svc;
+ #return { error => 'Only primary user may log in.' }
+ # if $conf->exists('selfservice_server-primary_only')
+ # && ( ! $pkg_svc || $pkg_svc->primary_svc ne 'Y' );
+ my $cust_svc = $svc_acct->cust_svc;
+ return { error => 'Only primary user may log in.' }
+ if $conf->exists('selfservice_server-primary_only')
+ && $cust_svc->svcpart != $cust_svc->cust_pkg->svcpart('svc_acct');
+
+ return { error => 'Incorrect password.' }
+ unless $svc_acct->check_password($p->{'password'});
+
+ $svc_x = $svc_acct;
+
+ }
my $session = {
- 'svcnum' => $svc_acct->svcnum,
+ 'svcnum' => $svc_x->svcnum,
};
- my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ my $cust_pkg = $svc_x->cust_svc->cust_pkg;
if ( $cust_pkg ) {
my $cust_main = $cust_pkg->cust_main;
$session->{'custnum'} = $cust_main->custnum;
$session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
} until ( ! defined _cache->get($session_id) ); #just in case
- _cache->set( $session_id, $session, '1 hour' );
+ my $timeout = $conf->config('selfservice-session_timeout') || '1 hour';
+ _cache->set( $session_id, $session, $timeout );
return { 'error' => '',
'session_id' => $session_id,
return { 'error' => $session } if $context eq 'error';
my %return;
+
+ my $conf = new FS::Conf;
+ if ($conf->exists('cust_main-require_address2')) {
+ $return{'require_address2'} = '1';
+ }else{
+ $return{'require_address2'} = '';
+ }
+
if ( $custnum ) { #customer record
my $search = { 'custnum' => $custnum };
} $cust_main->open_cust_bill;
$return{open_invoices} = \@open;
- my $conf = new FS::Conf;
$return{small_custview} =
small_custview( $cust_main, $conf->config('countrydefault') );
0 < ( grep { $_ eq 'POST' } $cust_main->invoicing_list );
if (scalar($conf->config('support_packages'))) {
- my $support = 0;
+ my @support_services = ();
foreach ($cust_main->support_services) {
- $support += $_->svc_x->seconds;
+ my $seconds = $_->svc_x->seconds;
+ my $time_remaining = (($seconds < 0) ? '-' : '' ).
+ int(abs($seconds)/3600)."h".
+ sprintf("%02d",(abs($seconds)%3600)/60)."m";
+ my $cust_pkg = $_->cust_pkg;
+ my $pkgnum = '';
+ my $pkg = '';
+ $pkgnum = $cust_pkg->pkgnum if $cust_pkg;
+ $pkg = $cust_pkg->part_pkg->pkg if $cust_pkg;
+ push @support_services, { svcnum => $_->svcnum,
+ time => $time_remaining,
+ pkgnum => $pkgnum,
+ pkg => $pkg,
+ };
}
- $return{support_time} = (($support < 0) ? '-' : '' ). int(abs($support)/3600)."h".sprintf("%02d",(abs($support)%3600)/60)."m";
+ $return{support_services} = \@support_services;
}
} elsif ( $session->{'svcnum'} ) { #no customer record
$new->set( $_ => $p->{$_} )
foreach grep { exists $p->{$_} } @cust_main_editable_fields;
- if ( $p->{'payby'} =~ /^(CARD|DCRD)$/ ) {
+ my $payby = '';
+ if (exists($p->{'payby'})) {
+ $p->{'payby'} =~ /^([A-Z]{4})$/
+ or return { 'error' => "illegal_payby " . $p->{'payby'} };
+ $payby = $1;
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ ) {
+
$new->paydate($p->{'year'}. '-'. $p->{'month'}. '-01');
+
if ( $new->payinfo eq $cust_main->paymask ) {
$new->payinfo($cust_main->payinfo);
} else {
- $new->paycvv($p->{'paycvv'});
+ $new->payinfo($p->{'payinfo'});
}
+
+ $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
+
+ }elsif ( $payby =~ /^(CHEK|DCHK)$/ ) {
+ my $payinfo;
+ $p->{'payinfo1'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
+ my $payinfo1 = $1;
+ $p->{'payinfo2'} =~ /^([\dx]+)$/
+ or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
+ my $payinfo2 = $1;
+ $payinfo = $payinfo1. '@'. $payinfo2;
+
+ if ( $payinfo eq $cust_main->paymask ) {
+ $new->payinfo($cust_main->payinfo);
+ } else {
+ $new->payinfo($payinfo);
+ }
+
+ $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' );
+
+ }elsif ( $payby =~ /^(BILL)$/ ) {
+ } elsif ( $payby ) { #notyet ready
+ return { 'error' => "unknown payby $payby" };
}
my @invoicing_list;
'paytypes' => [ @FS::cust_main::paytypes ],
+ 'paybys' => [ $conf->config('signup_server-payby') ],
+
'stateid_label' => FS::Msgcat::_gettext('stateid'),
'stateid_state_label' => FS::Msgcat::_gettext('stateid_state'),
if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
$return{card_type} = cardtype($cust_main->payinfo);
- $return{payinfo} = $cust_main->payinfo;
+ $return{payinfo} = $cust_main->paymask;
@return{'month', 'year'} = $cust_main->paydate_monthyear;
}
if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
- my ($payinfo1, $payinfo2) = split '@', $cust_main->payinfo;
+ my ($payinfo1, $payinfo2) = split '@', $cust_main->paymask;
$return{payinfo1} = $payinfo1;
$return{payinfo2} = $payinfo2;
$return{paytype} = $cust_main->paytype;
or return { 'error' => "illegal_payby " . $p->{'payby'} };
my $payby = $1;
+ #false laziness w/process/payment.cgi
my $payinfo;
my $paycvv = '';
if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
- $p->{'payinfo1'} =~ /^(\d+)$/
+ $p->{'payinfo1'} =~ /^([\dx]+)$/
or return { 'error' => "illegal account number ". $p->{'payinfo1'} };
my $payinfo1 = $1;
- $p->{'payinfo2'} =~ /^(\d+)$/
+ $p->{'payinfo2'} =~ /^([\dx]+)$/
or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} };
my $payinfo2 = $1;
$payinfo = $payinfo1. '@'. $payinfo2;
+
+ $payinfo = $cust_main->payinfo
+ if $cust_main->paymask eq $payinfo;
} elsif ( $payby eq 'CARD' || $payby eq 'DCRD' ) {
$payinfo = $p->{'payinfo'};
+
+ $payinfo = $cust_main->payinfo
+ if $cust_main->paymask eq $payinfo;
+
$payinfo =~ s/\D//g;
$payinfo =~ /^(\d{13,16})$/
or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo
$payinfo = $1;
+
validate($payinfo)
or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo
return { 'error' => gettext('unknown_card_type') }
}
+sub process_payment_order_pkg {
+ my $p = shift;
+
+ my $hr = process_payment($p);
+ return $hr if $hr->{'error'};
+
+ order_pkg($p);
+}
+
sub process_prepay {
my $p = shift;
return { 'error' => '',
'invnum' => $invnum,
'invoice_text' => join('', $cust_bill->print_text ),
- 'invoice_html' => $cust_bill->print_html,
+ 'invoice_html' => $cust_bill->print_html( { unsquelch_cdr => 1 } ),
};
}
}
+sub _list_svc_usage {
+ my($svc_acct, $begin, $end) = @_;
+ my @usage = ();
+ foreach my $part_export (
+ map { qsearch ( 'part_export', { 'exporttype' => $_ } ) }
+ qw (sqlradius sqlradius_withdomain')
+ ) {
+
+ push @usage, @ { $part_export->usage_sessions($begin, $end, $svc_acct) };
+ }
+ (@usage);
+}
+
sub list_svc_usage {
- my $p = shift;
+ _usage_details(\&_list_svc_usage, @_);
+}
+
+sub _list_support_usage {
+ my($svc_acct, $begin, $end) = @_;
+ my @usage = ();
+ foreach ( grep { $begin <= $_->_date && $_->_date <= $end }
+ qsearch('acct_rt_transaction', { 'svcnum' => $svc_acct->svcnum })
+ ) {
+ push @usage, { 'seconds' => $_->seconds,
+ 'support' => $_->support,
+ '_date' => $_->_date,
+ 'id' => $_->transaction_id,
+ 'creator' => $_->creator,
+ 'subject' => $_->subject,
+ 'status' => $_->status,
+ 'ticketid' => $_->ticketid,
+ };
+ }
+ (@usage);
+}
+
+sub list_support_usage {
+ _usage_details(\&_list_support_usage, @_);
+}
+
+sub _usage_details {
+ my ($callback, $p) = (shift,shift);
my($context, $session, $custnum) = _custoragent_session_custnum($p);
return { 'error' => $session } if $context eq 'error';
$p->{beginning} = $svc_acct->cust_svc->cust_pkg->last_bill;
$p->{ending} = $end;
}
- my @usage = ();
- foreach my $part_export (
- map { qsearch ( 'part_export', { 'exporttype' => $_ } ) }
- qw (sqlradius sqlradius_withdomain')
- ) {
-
- push @usage, @ { $part_export->usage_sessions($p->{beginning},
- $p->{ending},
- $svc_acct)
- };
- }
+ my (@usage) = &$callback($svc_acct,$p->{beginning},$p->{ending});
#kinda false laziness with FS::cust_main::bill, but perhaps
#we should really change this bit to DateTime and DateTime::Duration
my $previous = timelocal_nocheck($psec,$pmin,$phour,$pmday,$pmon,$pyear);
my $next = timelocal_nocheck($nsec,$nmin,$nhour,$nmday,$nmon,$nyear);
-
{
'error' => '',
'svcnum' => $p->{svcnum},
my $cust_main = qsearchs('cust_main', $search )
or return { 'error' => "unknown custnum $custnum" };
+ my $status = $cust_main->status;
#false laziness w/ClientAPI/Signup.pm
my $cust_pkg = new FS::cust_pkg ( {
my $conf = new FS::Conf;
if ( $conf->exists('signup_server-realtime') ) {
- my $bill_error = _do_bop_realtime( $cust_main );
+ my $bill_error = _do_bop_realtime( $cust_main, $status );
if ($bill_error) {
$cust_pkg->cancel('quiet'=>1);
my $cust_main = qsearchs('cust_main', $search )
or return { 'error' => "unknown custnum $custnum" };
+ my $status = $cust_main->status;
my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $p->{pkgnum} } )
or return { 'error' => "unknown package $p->{pkgnum}" };
my $conf = new FS::Conf;
if ( $conf->exists('signup_server-realtime') ) {
- my $bill_error = _do_bop_realtime( $cust_main );
+ my $bill_error = _do_bop_realtime( $cust_main, $status );
if ($bill_error) {
$newpkg[0]->suspend;
my $cust_main = qsearchs('cust_main', $search )
or return { 'error' => "unknown custnum $custnum" };
+ my $status = $cust_main->status;
my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $p->{'svcnum'} } )
or return { 'error' => "unknown service " . $p->{'svcnum'} };
my $conf = new FS::Conf;
if ( $conf->exists('signup_server-realtime') && !$bill_error ) {
- $bill_error = _do_bop_realtime( $cust_main );
+ $bill_error = _do_bop_realtime( $cust_main, $status );
if ($bill_error) {
return $bill_error;
}
sub _do_bop_realtime {
- my ($cust_main) = @_;
+ my ($cust_main, $status) = (shift, shift);
my $old_balance = $cust_main->balance;
if ( $cust_main->balance > $old_balance
&& $cust_main->balance > 0
- && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) {
+ && ( $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ?
+ 1 : $status eq 'suspended' ) ) {
#this makes sense. credit is "un-doing" the invoice
+ my $conf = new FS::Conf;
$cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ),
- 'self-service decline' );
+ 'self-service decline',
+ 'reason_type' => $conf->config('signup_credit_type'),
+ );
$cust_main->apply_credits( 'order' => 'newest' );
return { 'error' => '_decline', 'bill_error' => $bill_error };
sub provision_acct {
my $p = shift;
+ warn "provision_acct called\n"
+ if $DEBUG;
return { 'error' => gettext('passwords_dont_match') }
if $p->{'_password'} ne $p->{'_password2'};
return { 'error' => gettext('empty_password') }
unless length($p->{'_password'});
+
+ if ($p->{'domsvc'}) {
+ my %domains = domain_select_hash FS::svc_acct(map { $_ => $p->{$_} }
+ qw ( svcpart pkgnum ) );
+ return { 'error' => gettext('invalid_domain') }
+ unless ($domains{$p->{'domsvc'}});
+ }
+ warn "provision_acct calling _provision\n"
+ if $DEBUG;
_provision( 'FS::svc_acct',
- [qw(username _password)],
- [qw(username _password)],
+ [qw(username _password domsvc)],
+ [qw(username _password domsvc)],
$p,
@_
);
sub _provision {
my( $class, $fields, $return_fields, $p ) = splice(@_, 0, 4);
+ warn "_provision called for $class\n"
+ if $DEBUG;
my($context, $session, $custnum) = _custoragent_session_custnum($p);
return { 'error' => $session } if $context eq 'error';
my $pkgnum = $p->{'pkgnum'};
+ warn "searching for custnum $custnum pkgnum $pkgnum\n"
+ if $DEBUG;
my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
'pkgnum' => $pkgnum,
} )
or return { 'error' => "unknown pkgnum $pkgnum" };
+ warn "searching for svcpart ". $p->{'svcpart'}. "\n"
+ if $DEBUG;
my $part_svc = qsearchs('part_svc', { 'svcpart' => $p->{'svcpart'} } )
or return { 'error' => "unknown svcpart $p->{'svcpart'}" };
+ warn "creating $class record\n"
+ if $DEBUG;
my $svc_x = $class->new( {
'pkgnum' => $p->{'pkgnum'},
'svcpart' => $p->{'svcpart'},
map { $_ => $p->{$_} } @$fields
} );
+ warn "inserting $class record\n"
+ if $DEBUG;
my $error = $svc_x->insert;
- $svc_x = qsearchs($svc_x->table, { 'svcnum' => $svc_x->svcnum })
- unless $error;
- return { 'svc' => $part_svc->svc,
- 'error' => $error,
- map { $_ => $svc_x->get($_) } @$return_fields
- };
+ unless ( $error ) {
+ warn "finding inserted record for svcnum ". $svc_x->svcnum. "\n"
+ if $DEBUG;
+ $svc_x = qsearchs($svc_x->table, { 'svcnum' => $svc_x->svcnum })
+ }
+
+ my $return = { 'svc' => $part_svc->svc,
+ 'error' => $error,
+ map { $_ => $svc_x->get($_) } @$return_fields
+ };
+ warn "_provision returning ". Dumper($return). "\n"
+ if $DEBUG;
+ return $return;
}