X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FMyAccount.pm;h=784a33f616171ce8f0ebbc08e7d5bdea2ac8bf16;hb=4ecf0133faca10a1e959fd0eeb285ce4f9cae212;hp=fa667c0c08ed0e9e10d2cca6329d5ad4447ded21;hpb=b278e5d654ee0e1a429fa6177e69c8ab793181bb;p=freeside.git diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index fa667c0c0..784a33f61 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -4,12 +4,16 @@ use 5.008; #require 5.8+ for Time::Local 1.05+ use strict; use vars qw( $cache $DEBUG $me ); use subs qw( _cache _provision ); +use IO::Scalar; use Data::Dumper; use Digest::MD5 qw(md5_hex); use Date::Format; -use Business::CreditCard; use Time::Duration; use Time::Local qw(timelocal_nocheck); +use Business::CreditCard; +use HTML::Entities; +use Text::CSV_XS; +use Spreadsheet::WriteExcel; use FS::UI::Web::small_custview qw(small_custview); #less doh use FS::UI::Web; use FS::UI::bytecount qw( display_bytecount ); @@ -19,24 +23,24 @@ use FS::Record qw(qsearch qsearchs dbh); use FS::Msgcat qw(gettext); use FS::Misc qw(card_types); use FS::Misc::DateTime qw(parse_datetime); +use FS::TicketSystem; use FS::ClientAPI_SessionCache; use FS::cust_svc; use FS::svc_acct; use FS::svc_domain; use FS::svc_phone; use FS::svc_external; +use FS::svc_dsl; +use FS::dsl_device; use FS::part_svc; use FS::cust_main; use FS::cust_bill; +use FS::legacy_cust_bill; use FS::cust_main_county; use FS::cust_pkg; use FS::payby; use FS::acct_rt_transaction; -use HTML::Entities; -use FS::TicketSystem; -use Text::CSV_XS; -use IO::Scalar; -use Spreadsheet::WriteExcel; +use FS::msg_template; $DEBUG = 0; $me = '[FS::ClientAPI::MyAccount]'; @@ -44,13 +48,23 @@ $me = '[FS::ClientAPI::MyAccount]'; use vars qw( @cust_main_editable_fields ); @cust_main_editable_fields = qw( first last company address1 address2 city - county state zip country daytime night fax + county state zip country + daytime night fax mobile ship_first ship_last ship_company ship_address1 ship_address2 ship_city - ship_state ship_zip ship_country ship_daytime ship_night ship_fax + ship_state ship_zip ship_country + ship_daytime ship_night ship_fax ship_mobile + locale payby payinfo payname paystart_month paystart_year payissue payip ss paytype paystate stateid stateid_state ); +BEGIN { #preload to reduce time customer_info takes + if ( $FS::TicketSystem::system ) { + warn "$me: initializing ticket system\n" if $DEBUG; + FS::TicketSystem->init(); + } +} + sub _cache { $cache ||= new FS::ClientAPI_SessionCache( { 'namespace' => 'FS::ClientAPI::MyAccount', @@ -239,6 +253,25 @@ sub logout { } } +sub switch_acct { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $svc_acct = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_acct' ) + or return { 'error' => "Service not found" }; + + $session->{'svcnum'} = $svc_acct->svcnum; + + my $conf = new FS::Conf; + my $timeout = $conf->config('selfservice-session_timeout') || '1 hour'; + _cache->set( $p->{'session_id'}, $session, $timeout ); + + return { 'error' => '' }; + +} + sub payment_gateway { # internal use only # takes a cust_main and a cust_payby entry, returns the payment_gateway @@ -378,6 +411,7 @@ sub customer_info { ); $return{name} = $cust_main->first. ' '. $cust_main->get('last'); + $return{ship_name} = $cust_main->ship_first. ' '. $cust_main->get('ship_last'); for (@cust_main_editable_fields) { $return{$_} = $cust_main->get($_); @@ -422,6 +456,7 @@ sub customer_info { if ( $session->{'svcnum'} ) { my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $session->{'svcnum'} }); $return{'svc_label'} = ($cust_svc->label)[1] if $cust_svc; + $return{'svcnum'} = $session->{'svcnum'}; } } elsif ( $session->{'svcnum'} ) { #no customer record @@ -443,6 +478,72 @@ sub customer_info { } +sub customer_info_short { + 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; + + if ( $custnum ) { #customer record + + 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" }; + + $return{small_custview} = + small_custview( $cust_main, + scalar($conf->config('countrydefault')), + 1, ##nobalance + ); + + $return{name} = $cust_main->first. ' '. $cust_main->get('last'); + $return{ship_name} = $cust_main->ship_first. ' '. $cust_main->get('ship_last'); + + $return{payby} = $cust_main->payby; + + #none of these are terribly expensive if we want 'em... + for (@cust_main_editable_fields) { + $return{$_} = $cust_main->get($_); + } + + if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) { + $return{payinfo} = $cust_main->paymask; + @return{'month', 'year'} = $cust_main->paydate_monthyear; + } + + $return{'invoicing_list'} = + join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ); + #$return{'postal_invoicing'} = + # 0 < ( grep { $_ eq 'POST' } $cust_main->invoicing_list ); + + if ( $session->{'svcnum'} ) { + my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $session->{'svcnum'} }); + $return{'svc_label'} = ($cust_svc->label)[1] if $cust_svc; + $return{'svcnum'} = $session->{'svcnum'}; + } + + } elsif ( $session->{'svcnum'} ) { #no customer record + + #uuh, not supproted yet... die? + return { 'error' => 'customer_info_short not yet supported as agent' }; + + } else { + + return { 'error' => 'Expired session' }; #XXX redirect to login w/this err! + + } + + return { 'error' => '', + 'custnum' => $custnum, + %return, + }; +} + sub edit_info { my $p = shift; my $session = _cache->get($p->{'session_id'}) @@ -483,7 +584,7 @@ sub edit_info { $p->{'payinfo1'} =~ /^([\dx]+)$/ or return { 'error' => "illegal account number ". $p->{'payinfo1'} }; my $payinfo1 = $1; - $p->{'payinfo2'} =~ /^([\dx]+)$/ + $p->{'payinfo2'} =~ /^([\dx\.]+)$/ # . turned on by -require-bank-branch? or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} }; my $payinfo2 = $1; $payinfo = $payinfo1. '@'. $payinfo2; @@ -630,19 +731,17 @@ sub payment_info { %return, }; -}; +} #some false laziness with httemplate/process/payment.cgi - look there for #ACH and CVV support stuff -sub process_payment { +sub validate_payment { my $p = shift; my $session = _cache->get($p->{'session_id'}) or return { 'error' => "Can't resume session" }; #better error message - my %return; - my $custnum = $session->{'custnum'}; my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) @@ -673,7 +772,6 @@ sub process_payment { #false laziness w/process/payment.cgi my $payinfo; my $paycvv = ''; - my $paynum = ''; if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) { $p->{'payinfo1'} =~ /^([\dx]+)$/ @@ -728,38 +826,104 @@ sub process_payment { 'CHEK' => [ qw( ss paytype paystate stateid stateid_state payip ) ], ); + my $card_type = ''; + $card_type = cardtype($payinfo) if $payby eq 'CARD'; + + { + 'cust_main' => $cust_main, #XXX or just custnum?? + 'amount' => $amount, + 'payby' => $payby, + 'payinfo' => $payinfo, + 'paymask' => $cust_main->mask_payinfo( $payby, $payinfo ), + 'card_type' => $card_type, + 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01', + 'paydate_pretty' => $p->{'month'}. ' / '. $p->{'year'}, + 'payname' => $payname, + 'paybatch' => $paybatch, #this doesn't actually do anything + 'paycvv' => $paycvv, + 'payname' => $payname, + 'discount_term' => $discount_term, + 'pkgnum' => $session->{'pkgnum'}, + map { $_ => $p->{$_} } ( @{ $payby2fields{$payby} }, + qw( save auto ), + ) + }; + +} + +sub store_payment { + my $p = shift; + + my $validate = validate_payment($p); + return $validate if $validate->{'error'}; + + my $conf = new FS::Conf; + my $timeout = $conf->config('selfservice-session_timeout') || '1 hour'; #? + _cache->set( 'payment_'.$p->{'session_id'}, $validate, $timeout ); + + +{ map { $_=>$validate->{$_} } + qw( card_type paymask payname paydate_pretty amount ) + }; + +} + +sub process_stored_payment { + my $p = shift; + + my $session_id = $p->{'session_id'}; + + my $payment_info = _cache->get( "payment_$session_id" ) + or return { 'error' => "Can't resume session" }; #better error message + + do_process_payment($payment_info); + +} + +sub process_payment { + my $p = shift; + + my $payment_info = validate_payment($p); + return $payment_info if $payment_info->{'error'}; + + do_process_payment($payment_info); + +} + +sub do_process_payment { + my $validate = shift; + + my $cust_main = $validate->{'cust_main'}; + + my $amount = delete $validate->{'amount'}; + my $paynum = ''; + + my $payby = delete $validate->{'payby'}; + my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount, - 'quiet' => 1, - 'payinfo' => $payinfo, - 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01', - 'payname' => $payname, - 'paybatch' => $paybatch, #this doesn't actually do anything - 'paycvv' => $paycvv, - 'paynum_ref' => \$paynum, - 'pkgnum' => $session->{'pkgnum'}, - 'discount_term' => $discount_term, + 'quiet' => 1, 'selfservice' => 1, - map { $_ => $p->{$_} } @{ $payby2fields{$payby} } + 'paynum_ref' => \$paynum, + %$validate, ); return { 'error' => $error } if $error; $cust_main->apply_payments; - if ( $p->{'save'} ) { + if ( $validate->{'save'} ) { my $new = new FS::cust_main { $cust_main->hash }; - if ($payby eq 'CARD' || $payby eq 'DCRD') { - $new->set( $_ => $p->{$_} ) + if ($validate->{'payby'} eq 'CARD' || $validate->{'payby'} eq 'DCRD') { + $new->set( $_ => $validate->{$_} ) foreach qw( payname paystart_month paystart_year payissue payip address1 address2 city state zip country ); - $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' ); + $new->set( 'payby' => $validate->{'auto'} ? 'CARD' : 'DCRD' ); } elsif ($payby eq 'CHEK' || $payby eq 'DCHK') { - $new->set( $_ => $p->{$_} ) + $new->set( $_ => $validate->{$_} ) foreach qw( payname payip paytype paystate stateid stateid_state ); - $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' ); + $new->set( 'payby' => $validate->{'auto'} ? 'CHEK' : 'DCHK' ); } - $new->set( 'payinfo' => $cust_main->card_token || $payinfo ); - $new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' ); + $new->set( 'payinfo' => $cust_main->card_token || $validate->{'payinfo'} ); + $new->set( 'paydate' => $validate->{'paydate'} ); my $error = $new->replace($cust_main); if ( $error ) { #no, this causes customers to process their payments again @@ -767,21 +931,22 @@ sub process_payment { #XXX just warn verosely for now so i can figure out how these happen in # the first place, eventually should redirect them to the "change #address" page but indicate the payment did process?? - delete($p->{'payinfo'}); #don't want to log this! + delete($validate->{'payinfo'}); #don't want to log this! warn "WARNING: error changing customer info when processing payment (not returning to customer as a processing error): $error\n". "NEW: ". Dumper($new)."\n". "OLD: ". Dumper($cust_main)."\n". - "PACKET: ". Dumper($p)."\n"; + "PACKET: ". Dumper($validate)."\n"; #} else { #not needed... #$cust_main = $new; } } + my $cust_pay = ''; my $receipt_html = ''; - if($paynum) { + if ($paynum) { # currently supported for realtime CC only; send receipt data to SS - my $cust_pay = qsearchs('cust_pay', { 'paynum' => $paynum } ); + $cust_pay = qsearchs('cust_pay', { 'paynum' => $paynum } ); if($cust_pay) { $receipt_html = qq!
Amount | -! . $cust_pay->paid . qq! | +! . sprintf('%.2f', $cust_pay->paid) . qq! |