X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FMyAccount.pm;h=7e1720da5ca4fd79ea20bca860de6a24f9eec728;hp=824ff67cb887bdcb56afd395eaff6eb9eb99a676;hb=f32ac83068c6211f829f1688a1a9cdec71bc6ec7;hpb=6419542b10f8ebb0dada9dcb1a48cf78151ca82a diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index 824ff67cb..7e1720da5 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -61,8 +61,7 @@ use vars qw( @cust_main_editable_fields @location_editable_fields ); @cust_main_editable_fields = qw( first last company daytime night fax mobile locale - payby payinfo payname paystart_month paystart_year payissue payip - ss paytype paystate stateid stateid_state + ss stateid stateid_state ); @location_editable_fields = qw( address1 address2 city county state zip country @@ -627,8 +626,6 @@ sub customer_info_short { $return{'last'} = $cust_main->get('last'); $return{name} = $cust_main->first. ' '. $cust_main->get('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($_); @@ -641,11 +638,6 @@ sub customer_info_short { if $cust_main->ship_locationnum; } - if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) { - $return{payinfo} = $cust_main->paymask; - @return{'month', 'year'} = $cust_main->paydate_monthyear; - } - my @invoicing_list = $cust_main->invoicing_list; $return{'invoicing_list'} = join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ); @@ -669,6 +661,11 @@ sub customer_info_short { } + # this is here because this routine is called by both fs_ and ng_ main pages, where it appears + # it is not customer-specific, though it is only shown to authenticated customers + # it is not currently agent-specific, though at some point it might be + $return{'announcement'} = join(' ',$conf->config('selfservice-announcement')) || ''; + return { 'error' => '', 'custnum' => $custnum, %return, @@ -756,55 +753,8 @@ sub edit_info { # but if it hasn't been passed in at all, leave ship_location alone-- # DON'T change it to match bill_location. - my $payby = ''; - if (exists($p->{'payby'})) { - $p->{'payby'} =~ /^([A-Z]{4})$/ - or return { 'error' => "illegal_payby " . $p->{'payby'} }; - $payby = $1; - } - my $conf = new FS::Conf; - if ( $payby =~ /^(CARD|DCRD)$/ ) { - - $new->paydate($p->{'year'}. '-'. $p->{'month'}. '-01'); - - if ( $new->payinfo eq $cust_main->paymask ) { - $new->payinfo($cust_main->payinfo); - $new->paycvv( $p->{'paycvv'} || $cust_main->paycvv ); - } else { - $new->payinfo($p->{'payinfo'}); - return { 'error' => 'CVV2 is required' } - if ! $p->{'paycvv'} && $conf->exists('selfservice-onfile_require_cvv'); - $new->paycvv( $p->{'paycvv'} ) - } - - $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\.]+)$/ # . turned on by echeck-country CA ? - or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} }; - my $payinfo2 = $1; - $payinfo = $payinfo1. '@'. $payinfo2; - - $new->payinfo( ($payinfo eq $cust_main->paymask) - ? $cust_main->payinfo - : $payinfo - ); - - $new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' ); - - } elsif ( $payby =~ /^(BILL)$/ ) { - #no-op - } elsif ( $payby ) { #notyet ready - return { 'error' => "unknown payby $payby" }; - } - my @invoicing_list; if ( exists $p->{'invoicing_list'} || exists $p->{'postal_invoicing'} ) { #false laziness with httemplate/edit/process/cust_main.cgi @@ -900,31 +850,30 @@ sub payment_info { $return{balance} = $cust_main->balance; #XXX pkg-balances? - $return{payname} = $cust_main->payname - || ( $cust_main->first. ' '. $cust_main->get('last') ); - $return{$_} = $cust_main->bill_location->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); - $return{payinfo} = $cust_main->paymask; - - @return{'month', 'year'} = $cust_main->paydate_monthyear; - - } - - if ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) { - my ($payinfo1, $payinfo2) = split '@', $cust_main->paymask; - $return{payinfo1} = $payinfo1; - $return{payinfo2} = $payinfo2; - $return{paytype} = $cust_main->paytype; - $return{paystate} = $cust_main->paystate; - $return{payname} = $cust_main->payname; # override 'first/last name' default from above, if any. Is instution-name here. (#15819) - } + #XXX look for stored cust_payby info + # + # $return{payname} = $cust_main->payname + # || ( $cust_main->first. ' '. $cust_main->get('last') ); + # + #if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) { + # $return{card_type} = cardtype($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->paymask; + # $return{payinfo1} = $payinfo1; + # $return{payinfo2} = $payinfo2; + # $return{paytype} = $cust_main->paytype; + # $return{paystate} = $cust_main->paystate; + # $return{payname} = $cust_main->payname; # override 'first/last name' default from above, if any. Is instution-name here. (#15819) + #} if ( $conf->config('prepayment_discounts-credit_type') ) { #need to eval? @@ -1142,37 +1091,6 @@ sub do_process_payment { my $payby = delete $validate->{'payby'}; - my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount, - 'quiet' => 1, - 'manual' => 1, - 'selfservice' => 1, - 'paynum_ref' => \$paynum, - %$validate, - ); - return { 'error' => $error } if $error; - - #no error, so order the fee package if applicable... - my $conf = new FS::Conf; - my $fee_pkgpart = $conf->config('selfservice_process-pkgpart', $cust_main->agentnum); - my $fee_skip_first = $conf->exists('selfservice_process-skip_first'); - - if ( $fee_pkgpart and ! $fee_skip_first || scalar($cust_main->cust_pay) ) { - - my $cust_pkg = new FS::cust_pkg { 'pkgpart' => $fee_pkgpart }; - - $error = $cust_main->order_pkg( 'cust_pkg' => $cust_pkg ); - return { 'error' => "payment processed successfully, but error ordering fee: $error" } - if $error; - - #and generate an invoice for it now too - $error = $cust_main->bill( 'pkg_list' => [ $cust_pkg ] ); - return { 'error' => "payment processed and fee ordered sucessfully, but error billing fee: $error" } - if $error; - - } - - $cust_main->apply_payments; - if ( $validate->{'save'} ) { my $new = new FS::cust_main { $cust_main->hash }; if ($payby eq 'CARD' || $payby eq 'DCRD') { @@ -1193,7 +1111,7 @@ sub do_process_payment { stateid stateid_state ); $new->set( 'payby' => $validate->{'auto'} ? 'CHEK' : 'DCHK' ); } - $new->set( 'payinfo' => $cust_main->card_token || $validate->{'payinfo'} ); + $new->payinfo( $validate->{'payinfo'} ); #to properly set paymask $new->set( 'paydate' => $validate->{'paydate'} ); my $error = $new->replace($cust_main); if ( $error ) { @@ -1201,18 +1119,48 @@ sub do_process_payment { #return { 'error' => $error }; #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?? + #address" page but indicate if the payment processed? 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($validate)."\n"; - #} else { - #not needed... - #$cust_main = $new; + } else { + $cust_main = $new; } } + my $error = $cust_main->realtime_bop( $FS::payby::payby2bop{$payby}, $amount, + 'quiet' => 1, + 'manual' => 1, + 'selfservice' => 1, + 'paynum_ref' => \$paynum, + %$validate, + ); + return { 'error' => $error } if $error; + + #no error, so order the fee package if applicable... + my $conf = new FS::Conf; + my $fee_pkgpart = $conf->config('selfservice_process-pkgpart', $cust_main->agentnum); + my $fee_skip_first = $conf->exists('selfservice_process-skip_first'); + + if ( $fee_pkgpart and ! $fee_skip_first || scalar($cust_main->cust_pay) ) { + + my $cust_pkg = new FS::cust_pkg { 'pkgpart' => $fee_pkgpart }; + + $error = $cust_main->order_pkg( 'cust_pkg' => $cust_pkg ); + return { 'error' => "payment processed successfully, but error ordering fee: $error" } + if $error; + + #and generate an invoice for it now too + $error = $cust_main->bill( 'pkg_list' => [ $cust_pkg ] ); + return { 'error' => "payment processed and fee ordered sucessfully, but error billing fee: $error" } + if $error; + + } + + $cust_main->apply_payments; + my $cust_pay = ''; my $receipt_html = ''; if ($paynum) { @@ -1546,7 +1494,6 @@ sub invoice_logo { }; } - sub list_invoices { my $p = shift; my $session = _cache->get($p->{'session_id'}) @@ -1606,6 +1553,79 @@ sub list_invoices { }; } +sub list_payby { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) + or return { 'error' => "unknown custnum $custnum" }; + + return { + 'payby' => [ map { + my $cust_payby = $_; + +{ + map { $_ => $cust_payby->$_ } + qw( custpaybynum weight payby paymask paydate + payname paystate paytype + ) + }; + } + $cust_main->cust_payby + ], + }; +} + +sub insert_payby { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + #XXX payinfo1 + payinfo2 for CHEK? + #or take the opportunity to use separate, more well- named fields? + # my $payinfo; + # $p->{'payinfo1'} =~ /^([\dx]+)$/ + # or return { 'error' => "illegal account number ". $p->{'payinfo1'} }; + # my $payinfo1 = $1; + # $p->{'payinfo2'} =~ /^([\dx\.]+)$/ # . turned on by echeck-country CA ? + # or return { 'error' => "illegal ABA/routing number ". $p->{'payinfo2'} }; + # my $payinfo2 = $1; + # $payinfo = $payinfo1. '@'. $payinfo2; + + my $cust_payby = new FS::cust_payby { + 'custnum' => $custnum, + map { $_ => $p->{$_} } qw( weight payby payinfo paycvv paydate payname + paystate paytype payip + ), + }; + + my $error = $cust_payby->insert; + if ( $error ) { + return { 'error' => $error }; + } else { + return { 'custpaybynum' => $cust_payby->custpaybynum }; + } + +} + +sub delete_payby { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $cust_payby = qsearchs('cust_payby', { + 'custnum' => $custnum, + 'custpaybynum' => $p->{'custpaybynum'}, + }) + or return { 'error' => 'unknown custpaybynum '. $p->{'custpaybynum'} }; + + return { 'error' => $cust_payby->delete }; + +} + sub cancel { my $p = shift; my $session = _cache->get($p->{'session_id'}) @@ -2425,7 +2445,7 @@ sub change_pkg { if ( $conf->exists('signup_server-realtime') ) { - my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_credit'=>1 ); + my $bill_error = _do_bop_realtime( $cust_main, $status, 'no_invoice_void'=>1 ); if ($bill_error) { $err_or_cust_pkg->suspend; @@ -2499,35 +2519,36 @@ sub order_recharge { sub _do_bop_realtime { my ($cust_main, $status, %opt) = @_; - my $old_balance = $cust_main->balance; - - my $bill_error = $cust_main->bill - || $cust_main->apply_payments_and_credits; - - $bill_error ||= $cust_main->realtime_collect('selfservice' => 1) - if $cust_main->payby =~ /^(CARD|CHEK)$/; - - if ( $cust_main->balance > $old_balance - && $cust_main->balance > 0 - && ( $cust_main->payby !~ /^(BILL|DCRD|DCHK)$/ - || $status eq 'suspended' - ) - ) - { - unless ( $opt{'no_credit'} ) { - #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', - reason_type=>$conf->config('signup_credit_type'), - ); - $cust_main->apply_credits( 'order' => 'newest' ); + my $old_balance = $cust_main->balance; + + my @cust_bill; + my $bill_error = $cust_main->bill( + 'return_bill' => \@cust_bill, + ); + + $bill_error ||= $cust_main->apply_payments_and_credits; + + $bill_error ||= $cust_main->realtime_collect('selfservice' => 1); + + if ( $cust_main->balance > $old_balance + && $cust_main->balance > 0 + && ( $cust_main->has_cust_payby_auto || $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 }; + } + + ''; } sub renew_info { @@ -2974,13 +2995,15 @@ sub myaccount_passwd { ) && ! $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); - $svc_acct->set_password($p->{'new_password'}); - $error ||= $svc_acct->replace(); + $error ||= $svc_acct->is_password_allowed($p->{'new_password'}) + || $svc_acct->set_password($p->{'new_password'}) + || $svc_acct->replace(); #regular pw change in self-service should change contact pw too, otherwise its #way too confusing. hell its confusing they're separate at all, but alas. @@ -3032,7 +3055,7 @@ sub reset_passwd { my($username, $domain) = split('@', $p->{'email'}); my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } ); if ( $svc_domain ) { - $svc_acct = qsearchs('svc_acct', { 'username' => $p->{'username'}, + $svc_acct = qsearchs('svc_acct', { 'username' => $username, 'domsvc' => $svc_domain->svcnum } ); if ( $svc_acct ) { @@ -3120,7 +3143,7 @@ sub reset_passwd { my $reset_session = { 'svcnum' => $svc_acct->svcnum, - 'agentnum' => + 'agentnum' => $svc_acct->cust_main->agentnum, }; my $timeout = '1 hour'; #? @@ -3259,8 +3282,9 @@ sub process_reset_passwd { if ( $svc_acct ) { - $svc_acct->set_password($p->{'new_password'}); - my $error = $svc_acct->replace(); + my $error ||= $svc_acct->is_password_allowed($p->{'new_password'}) + || $svc_acct->set_password($p->{'new_password'}) + || $svc_acct->replace(); return { %$info, 'error' => $error } if $error;