X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FMyAccount.pm;h=9ee770b2070bb20a6c21948d4fe8d9f1b2aa5b84;hb=7cdcb8d249f66efdbd5eb4efdd6e07f0f7948b49;hp=de364724aedfe70bada324e01c3ea1f7aec6a7f2;hpb=dcdf657e77ec7b46dc69e19a849a9c133123db7c;p=freeside.git diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index de364724a..9ee770b20 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -9,6 +9,7 @@ use Business::CreditCard; use Time::Duration; use FS::CGI qw(small_custview); #doh use FS::UI::Web; +use FS::UI::bytecount; use FS::Conf; use FS::Record qw(qsearch qsearchs); use FS::Msgcat qw(gettext); @@ -22,8 +23,17 @@ use FS::cust_main; use FS::cust_bill; use FS::cust_main_county; use FS::cust_pkg; +use FS::payby; use HTML::Entities; +#false laziness with FS::cust_main +BEGIN { + eval "use Time::Local;"; + die "Time::Local minimum version 1.05 required with Perl versions before 5.6" + if $] < 5.006 && !defined($Time::Local::VERSION); + eval "use Time::Local qw(timelocal_nocheck);"; +} + use vars qw( @cust_main_editable_fields ); @cust_main_editable_fields = qw( first last company address1 address2 city @@ -228,6 +238,14 @@ sub payment_info { 'card_types' => card_types(), + 'paytypes' => [ @FS::cust_main::paytypes ], + + 'stateid_label' => FS::Msgcat::_gettext('stateid'), + 'stateid_state_label' => FS::Msgcat::_gettext('stateid_state'), + + 'show_ss' => $conf->exists('show_ss'), + 'show_stateid' => $conf->exists('show_stateid'), + 'show_paystate' => $conf->exists('show_bankstate'), }; } @@ -251,6 +269,7 @@ sub payment_info { $return{$_} = $cust_main->get($_) for qw(address1 address2 city state zip); $return{payby} = $cust_main->payby; + $return{stateid_state} = $cust_main->stateid_state; if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) { $return{card_type} = cardtype($cust_main->payinfo); @@ -260,6 +279,15 @@ sub payment_info { } + if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) { + my ($payinfo1, $payinfo2) = split '@', $cust_main->payinfo; + $return{payinfo1} = $payinfo1; + $return{payinfo2} = $payinfo2; + $return{paytype} = $cust_main->paytype; + $return{paystate} = $cust_main->paystate; + + } + #doubleclick protection my $_date = time; $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32; @@ -294,19 +322,23 @@ sub process_payment { or return { 'error' => gettext('illegal_text'). " paybatch: ". $p->{'paybatch'} }; my $paybatch = $1; + $p->{'payby'} =~ /^([A-Z]{4})$/ + or return { 'error' => "illegal_payby " . $p->{'payby'} }; + my $payby = $1; + my $payinfo; my $paycvv = ''; - #if ( $payby eq 'CHEK' ) { - # - # $p->{'payinfo1'} =~ /^(\d+)$/ - # or return { 'error' => "illegal account number ". $p->{'payinfo1'} }; - # my $payinfo1 = $1; - # $p->{'payinfo2'} =~ /^(\d+)$/ - # or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} }; - # my $payinfo2 = $1; - # $payinfo = $payinfo1. '@'. $payinfo2; - # - #} elsif ( $payby eq 'CARD' ) { + if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) { + + $p->{'payinfo1'} =~ /^(\d+)$/ + or return { 'error' => "illegal account number ". $p->{'payinfo1'} }; + my $payinfo1 = $1; + $p->{'payinfo2'} =~ /^(\d+)$/ + or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} }; + my $payinfo2 = $1; + $payinfo = $payinfo1. '@'. $payinfo2; + + } elsif ( $payby eq 'CARD' || $payby eq 'DCRD' ) { $payinfo = $p->{'payinfo'}; $payinfo =~ s/\D//g; @@ -318,33 +350,35 @@ sub process_payment { return { 'error' => gettext('unknown_card_type') } if cardtype($payinfo) eq "Unknown"; - if ( defined $cust_main->dbdef_table->column('paycvv') ) { - if ( length($p->{'paycvv'} ) ) { - if ( cardtype($payinfo) eq 'American Express card' ) { - $p->{'paycvv'} =~ /^(\d{4})$/ - or return { 'error' => "CVV2 (CID) for American Express cards is four digits." }; - $paycvv = $1; - } else { - $p->{'paycvv'} =~ /^(\d{3})$/ - or return { 'error' => "CVV2 (CVC2/CID) is three digits." }; - $paycvv = $1; - } + if ( length($p->{'paycvv'}) && $p->{'paycvv'} !~ /^\s*$/ ) { + if ( cardtype($payinfo) eq 'American Express card' ) { + $p->{'paycvv'} =~ /^\s*(\d{4})\s*$/ + or return { 'error' => "CVV2 (CID) for American Express cards is four digits." }; + $paycvv = $1; + } else { + $p->{'paycvv'} =~ /^\s*(\d{3})\s*$/ + or return { 'error' => "CVV2 (CVC2/CID) is three digits." }; + $paycvv = $1; } } - #} else { - # die "unknown payby $payby"; - #} + } else { + die "unknown payby $payby"; + } - my $error = $cust_main->realtime_bop( 'CC', $p->{'amount'}, + my %payby2fields = ( + 'CARD' => [ qw( paystart_month paystart_year payissue address1 address2 city state zip payip ) ], + 'CHEK' => [ qw( ss paytype paystate stateid stateid_state payip ) ], + ); + + my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $p->{'amount'}, 'quiet' => 1, 'payinfo' => $payinfo, 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01', 'payname' => $payname, 'paybatch' => $paybatch, 'paycvv' => $paycvv, - map { $_ => $p->{$_} } qw( paystart_month paystart_year payissue payip - address1 address2 city state zip ) + map { $_ => $p->{$_} } @{ $payby2fields{$payby} } ); return { 'error' => $error } if $error; @@ -352,11 +386,19 @@ sub process_payment { if ( $p->{'save'} ) { my $new = new FS::cust_main { $cust_main->hash }; - $new->set( $_ => $p->{$_} ) - foreach qw( payname paystart_month paystart_year payissue payip - address1 address2 city state zip payinfo ); + if ($payby eq 'CARD' || $payby eq 'DCRD') { + $new->set( $_ => $p->{$_} ) + foreach qw( payname paystart_month paystart_year payissue payip + address1 address2 city state zip payinfo ); + $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' ); + } elsif ($payby eq 'CHEK' || $payby eq 'DCHK') { + $new->set( $_ => $p->{$_} ) + foreach qw( payname payip paytype paystate + stateid stateid_state ); + $new->set( 'payinfo' => $payinfo ); + $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' ); + } $new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' ); - $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' ); my $error = $new->replace($cust_main); return { 'error' => $error } if $error; $cust_main = $new; @@ -396,11 +438,11 @@ sub process_prepay { 'seconds' => $seconds, 'duration' => duration_exact($seconds), 'upbytes' => $upbytes, - 'upload' => FS::UI::Web::bytecount_unexact($upbytes), + 'upload' => FS::UI::bytecount::bytecount_unexact($upbytes), 'downbytes' => $downbytes, - 'download' => FS::UI::Web::bytecount_unexact($downbytes), + 'download' => FS::UI::bytecount::bytecount_unexact($downbytes), 'totalbytes'=> $totalbytes, - 'totalload' => FS::UI::Web::bytecount_unexact($totalbytes), + 'totalload' => FS::UI::bytecount::bytecount_unexact($totalbytes), }; } @@ -538,8 +580,6 @@ sub list_pkgs { sub list_svcs { my $p = shift; - use Data::Dumper; - my($context, $session, $custnum) = _custoragent_session_custnum($p); return { 'error' => $session } if $context eq 'error'; @@ -575,14 +615,14 @@ sub list_svcs { 'username' => $svc_x->username, 'email' => $svc_x->email, 'seconds' => $svc_x->seconds, - 'upbytes' => $svc_x->upbytes, - 'downbytes' => $svc_x->downbytes, - 'totalbytes'=> $svc_x->totalbytes, - 'recharge_amount' => $part_pkg->option('recharge_amount'), - 'recharge_seconds' => $part_pkg->option('recharge_seconds'), - 'recharge_upbytes' => $part_pkg->option('recharge_upbytes'), - 'recharge_downbytes' => $part_pkg->option('recharge_downbytes'), - 'recharge_totalbytes' => $part_pkg->option('recharge_totalbytes'), + 'upbytes' => FS::UI::bytecount::display_bytecount($svc_x->upbytes), + 'downbytes' => FS::UI::bytecount::display_bytecount($svc_x->downbytes), + 'totalbytes'=> FS::UI::bytecount::display_bytecount($svc_x->totalbytes), + 'recharge_amount' => $part_pkg->option('recharge_amount', 1), + 'recharge_seconds' => $part_pkg->option('recharge_seconds', 1), + 'recharge_upbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_upbytes', 1)), + 'recharge_downbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_downbytes', 1)), + 'recharge_totalbytes' => FS::UI::bytecount::display_bytecount($part_pkg->option('recharge_totalbytes', 1)), # more... }; } @@ -592,6 +632,86 @@ sub list_svcs { } +sub list_svc_usage { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $search = { 'svcnum' => $p->{'svcnum'} }; + $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent'; + my $svc_acct = qsearchs ( 'svc_acct', $search ); + return { 'error' => 'No service selected in list_svc_usage' } + unless $svc_acct; + + my $freq = $svc_acct->cust_svc->cust_pkg->part_pkg->freq; + my $start = $svc_acct->cust_svc->cust_pkg->setup; + #my $end = $svc_acct->cust_svc->cust_pkg->bill; # or time? + my $end = time; + + unless($p->{beginning}){ + $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) + }; + } + + #kinda false laziness with FS::cust_main::bill, but perhaps + #we should really change this bit to DateTime and DateTime::Duration + # + #change this bit to use Date::Manip? CAREFUL with timezones (see + # mailing list archive) + my ($nsec,$nmin,$nhour,$nmday,$nmon,$nyear) = + (localtime($p->{ending}) )[0,1,2,3,4,5]; + my ($psec,$pmin,$phour,$pmday,$pmon,$pyear) = + (localtime($p->{beginning}) )[0,1,2,3,4,5]; + + if ( $freq =~ /^\d+$/ ) { + $nmon += $freq; + until ( $nmon < 12 ) { $nmon -= 12; $nyear++; } + $pmon -= $freq; + until ( $pmon >= 0 ) { $pmon += 12; $pyear--; } + } elsif ( $freq =~ /^(\d+)w$/ ) { + my $weeks = $1; + $nmday += $weeks * 7; + $pmday -= $weeks * 7; + } elsif ( $freq =~ /^(\d+)d$/ ) { + my $days = $1; + $nmday += $days; + $pmday -= $days; + } elsif ( $freq =~ /^(\d+)h$/ ) { + my $hours = $1; + $nhour += $hours; + $phour -= $hours; + } else { + return { 'error' => "unparsable frequency: ". $freq }; + } + + my $previous = timelocal_nocheck($psec,$pmin,$phour,$pmday,$pmon,$pyear); + my $next = timelocal_nocheck($nsec,$nmin,$nhour,$nmday,$nmon,$nyear); + + + { + 'error' => '', + 'svcnum' => $p->{svcnum}, + 'beginning' => $p->{beginning}, + 'ending' => $p->{ending}, + 'previous' => ($previous > $start) ? $previous : $start, + 'next' => ($next < $end) ? $next : $end, + 'usage' => \@usage, + }; +} + sub order_pkg { my $p = shift; @@ -628,7 +748,7 @@ sub order_pkg { $svcpart ||= $cust_pkg->part_pkg->svcpart($svcdb); my %fields = ( - 'svc_acct' => [ qw( username _password sec_phrase popnum ) ], + 'svc_acct' => [ qw( username domsvc _password sec_phrase popnum ) ], 'svc_domain' => [ qw( domain ) ], 'svc_external' => [ qw( id title ) ], ); @@ -674,23 +794,11 @@ sub order_pkg { my $conf = new FS::Conf; if ( $conf->exists('signup_server-realtime') ) { - my $old_balance = $cust_main->balance; - - my $bill_error = $cust_main->bill; - $cust_main->apply_payments; - $cust_main->apply_credits; - $bill_error = $cust_main->collect('realtime' => 1); - - if ( $cust_main->balance > $old_balance - && $cust_main->balance > 0 - && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) { - #this makes sense. credit is "un-doing" the invoice - $cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ), - 'self-service decline' ); - $cust_main->apply_credits( 'order' => 'newest' ); + my $bill_error = _do_bop_realtime( $cust_main ); + if ($bill_error) { $cust_pkg->cancel('quiet'=>1); - return { 'error' => '_decline', 'bill_error' => $bill_error }; + return $bill_error; } else { $cust_pkg->reexport; } @@ -703,6 +811,47 @@ sub order_pkg { } +sub change_pkg { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $search = { 'custnum' => $custnum }; + $search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent'; + my $cust_main = qsearchs('cust_main', $search ) + or return { 'error' => "unknown custnum $custnum" }; + + my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $p->{pkgnum} } ) + or return { 'error' => "unknown package $p->{pkgnum}" }; + + my @newpkg; + my $error = FS::cust_pkg::order( $custnum, + [$p->{pkgpart}], + [$p->{pkgnum}], + \@newpkg, + ); + + my $conf = new FS::Conf; + if ( $conf->exists('signup_server-realtime') ) { + + my $bill_error = _do_bop_realtime( $cust_main ); + + if ($bill_error) { + $newpkg[0]->suspend; + return $bill_error; + } else { + $newpkg[0]->reexport; + } + + } else { + $newpkg[0]->reexport; + } + + return { error => '', pkgnum => $cust_pkg->pkgnum }; + +} + sub order_recharge { my $p = shift; @@ -721,13 +870,11 @@ sub order_recharge { my $part_pkg = $cust_svc->cust_pkg->part_pkg; my %vhash = - map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_) } + map { $_ =~ /^recharge_(.*)$/; $1, $part_pkg->option($_, 1) } qw ( recharge_seconds recharge_upbytes recharge_downbytes recharge_totalbytes ); - my $amount = $part_pkg->option('recharge_amount'); + my $amount = $part_pkg->option('recharge_amount', 1); - my $old_balance = $cust_main->balance; - my ($l, $v, $d) = $cust_svc->label; # blah my $pkg = "Recharge $v"; @@ -739,22 +886,10 @@ sub order_recharge { my $conf = new FS::Conf; if ( $conf->exists('signup_server-realtime') && !$bill_error ) { - $bill_error = $cust_main->bill; - - $cust_main->apply_payments; - $cust_main->apply_credits; - $bill_error = $cust_main->collect('realtime' => 1); - - #false laziness with order_pkg - if ( $cust_main->balance > $old_balance - && $cust_main->balance > 0 - && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) { - #this makes sense. credit is "un-doing" the invoice - $cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ), - 'self-service decline' ); - $cust_main->apply_credits( 'order' => 'newest' ); + $bill_error = _do_bop_realtime( $cust_main ); - return { 'error' => '_decline', 'bill_error' => encode_entities($bill_error) }; + if ('bill_error') { + return $bill_error; } else { my $error = $svc_x->recharge (\%vhash); return { 'error' => $error } if $error; @@ -770,6 +905,30 @@ sub order_recharge { } +sub _do_bop_realtime { + my ($cust_main) = @_; + + my $old_balance = $cust_main->balance; + + my $bill_error = $cust_main->bill; + + $cust_main->apply_payments_and_credits; + $bill_error = $cust_main->collect('realtime' => 1); + + if ( $cust_main->balance > $old_balance + && $cust_main->balance > 0 + && $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ ) { + #this makes sense. credit is "un-doing" the invoice + $cust_main->credit( sprintf("%.2f", $cust_main->balance - $old_balance ), + 'self-service decline' ); + $cust_main->apply_credits( 'order' => 'newest' ); + + return { 'error' => '_decline', 'bill_error' => $bill_error }; + } + + ''; +} + sub cancel_pkg { my $p = shift; my $session = _cache->get($p->{'session_id'})