X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=78739bbb4c78169ee9e7c7a7d3b62a2c3d990bd3;hb=f8f4c51eaa6f5aa3d49672fe7a17f19fa22494c0;hp=684031c1d6382ae481668f1affc84d7049e6766a;hpb=a087f56c114ee266707275f1a5f2a94b60232865;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 684031c1d..78739bbb4 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -86,7 +86,7 @@ $skip_fuzzyfiles = 0; @fuzzyfields = ( 'first', 'last', 'company', 'address1' ); @encrypted_fields = ('payinfo', 'paycvv'); -sub nohistory_fields { ('paycvv'); } +sub nohistory_fields { ('payinfo', 'paycvv'); } @paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings'); @@ -1453,8 +1453,15 @@ sub replace { } - if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ && - grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) { + if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ + && ( ( $self->get('payinfo') ne $old->get('payinfo') + && $self->get('payinfo') !~ /^99\d{14}$/ + ) + || grep { $self->get($_) ne $old->get($_) } qw(paydate payname) + ) + ) + { + # card/check/lec info has changed, want to retry realtime_ invoice events my $error = $self->retry_realtime; if ( $error ) { @@ -1708,12 +1715,7 @@ sub check { # If it is encrypted and the private key is not availaible then we can't # check the credit card. - - my $check_payinfo = 1; - - if ($self->is_encrypted($self->payinfo)) { - $check_payinfo = 0; - } + my $check_payinfo = ! $self->is_encrypted($self->payinfo); if ( $check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) { @@ -1727,7 +1729,8 @@ sub check { or return gettext('invalid_card'); # . ": ". $self->payinfo; return gettext('unknown_card_type') - if cardtype($self->payinfo) eq "Unknown"; + if $self->payinfo !~ /^99\d{14}$/ #token + && cardtype($self->payinfo) eq "Unknown"; my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); if ( $ban ) { @@ -4239,6 +4242,7 @@ sub _bop_options { $options->{payment_gateway}->gatewaynum ? $options->{payment_gateway}->options : @{ $options->{payment_gateway}->get('options') }; + } sub _bop_defaults { @@ -4265,14 +4269,6 @@ sub _bop_content { my ($self, $options) = @_; my %content = (); - $content{address} = exists($options->{'address1'}) - ? $options->{'address1'} - : $self->address1; - my $address2 = exists($options->{'address2'}) - ? $options->{'address2'} - : $self->address2; - $content{address} .= ", ". $address2 if length($address2); - my $payip = exists($options->{'payip'}) ? $options->{'payip'} : $self->payip; $content{customer_ip} = $payip if length($payip); @@ -4283,14 +4279,30 @@ sub _bop_content { ( $conf->exists('business-onlinepayment-email_customer') || $conf->exists('business-onlinepayment-email-override') ); - $content{payfirst} = $self->getfield('first'); - $content{paylast} = $self->getfield('last'); + my ($payname, $payfirst, $paylast); + if ( $options->{payname} && $options->{method} ne 'ECHECK' ) { + ($payname = $options->{payname}) =~ + /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/ + or return "Illegal payname $payname"; + ($payfirst, $paylast) = ($1, $2); + } else { + $payfirst = $self->getfield('first'); + $paylast = $self->getfield('last'); + $payname = "$payfirst $paylast"; + } - $content{account_name} = "$content{payfirst} $content{paylast}" - if $options->{method} eq 'ECHECK'; + $content{last_name} = $paylast; + $content{first_name} = $payfirst; - $content{name} = $options->{payname}; - $content{name} = $content{account_name} if exists($content{account_name}); + $content{name} = $payname; + + $content{address} = exists($options->{'address1'}) + ? $options->{'address1'} + : $self->address1; + my $address2 = exists($options->{'address2'}) + ? $options->{'address2'} + : $self->address2; + $content{address} .= ", ". $address2 if length($address2); $content{city} = exists($options->{city}) ? $options->{city} @@ -4304,10 +4316,11 @@ sub _bop_content { $content{country} = exists($options->{country}) ? $options->{country} : $self->country; + $content{referer} = 'http://cleanwhisker.420.am/'; #XXX fix referer :/ $content{phone} = $self->daytime || $self->night; - (%content); + \%content; } my %bop_method2payby = ( @@ -4383,13 +4396,8 @@ sub realtime_bop { # massage data ### - my (%bop_content) = $self->_bop_content(\%options); - - if ( $options{method} ne 'ECHECK' ) { - $options{payname} =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/ - or return "Illegal payname $options{payname}"; - ($bop_content{payfirst}, $bop_content{paylast}) = ($1, $2); - } + my $bop_content = $self->_bop_content(\%options); + return $bop_content unless ref($bop_content); my @invoicing_list = $self->invoicing_list_emailonly; if ( $conf->exists('emailinvoiceautoalways') @@ -4455,6 +4463,9 @@ sub realtime_bop { $content{account_type} = exists($options{'paytype'}) ? uc($options{'paytype'}) || 'CHECKING' : uc($self->getfield('paytype')) || 'CHECKING'; + $content{account_name} = $self->getfield('first'). ' '. + $self->getfield('last'); + $content{customer_org} = $self->company ? 'B' : 'I'; $content{state_id} = exists($options{'stateid'}) ? $options{'stateid'} @@ -4539,7 +4550,7 @@ sub realtime_bop { 'amount' => $options{amount}, #'invoice_number' => $options{'invnum'}, 'customer_id' => $self->custnum, - %bop_content, + %$bop_content, 'reference' => $cust_pay_pending->paypendingnum, #for now 'email' => $email, %content, #after @@ -4554,6 +4565,8 @@ sub realtime_bop { my $BOP_TESTING_SUCCESS = 1; unless ( $BOP_TESTING ) { + $transaction->test_transaction(1) + if $conf->exists('business-onlinepayment-test_transaction'); $transaction->submit(); } else { if ( $BOP_TESTING_SUCCESS ) { @@ -4606,6 +4619,8 @@ sub realtime_bop { $capture->content( %capture ); + $capture->test_transaction(1) + if $conf->exists('business-onlinepayment-test_transaction'); $capture->submit(); unless ( $capture->is_success ) { @@ -4635,6 +4650,25 @@ sub realtime_bop { } ### + # Tokenize + ### + + + if ( $transaction->can('card_token') && $transaction->card_token ) { + + $self->card_token($transaction->card_token); + + if ( $options{'payinfo'} eq $self->payinfo ) { + $self->payinfo($transaction->card_token); + my $error = $self->replace; + if ( $error ) { + warn "WARNING: error storing token: $error, but proceeding anyway\n"; + } + } + + } + + ### # result handling ### @@ -4757,7 +4791,7 @@ sub _realtime_bop_result { 'paid' => $cust_pay_pending->paid, '_date' => '', 'payby' => $cust_pay_pending->payby, - #'payinfo' => $payinfo, + 'payinfo' => $options{'payinfo'}, 'paybatch' => $paybatch, 'paydate' => $cust_pay_pending->paydate, 'pkgnum' => $cust_pay_pending->pkgnum, @@ -5161,7 +5195,7 @@ sub realtime_refund_bop { my $self = shift; my %options = (); - if (ref($_[0]) ne 'HASH') { + if (ref($_[0]) eq 'HASH') { %options = %{$_[0]}; } else { my $method = shift; @@ -5293,6 +5327,8 @@ sub realtime_refund_bop { } } $void->content( 'action' => 'void', %content ); + $void->test_transaction(1) + if $conf->exists('business-onlinepayment-test_transaction'); $void->submit(); if ( $void->is_success ) { my $error = $cust_pay->void($options{'reason'}); @@ -5395,6 +5431,8 @@ sub realtime_refund_bop { ); warn join('', map { " $_ => $sub_content{$_}\n" } keys %sub_content ) if $DEBUG > 1; + $refund->test_transaction(1) + if $conf->exists('business-onlinepayment-test_transaction'); $refund->submit(); return "$processor error: ". $refund->error_message @@ -5833,29 +5871,17 @@ sub total_owed_date { my $self = shift; my $time = shift; -# my $custnum = $self->custnum; -# -# my $owed_sql = FS::cust_bill->owed_sql; -# -# my $sql = " -# SELECT SUM($owed_sql) FROM cust_bill -# WHERE custnum = $custnum -# AND _date <= $time -# "; -# -# my $sth = dbh->prepare($sql) or die dbh->errstr; -# $sth->execute() or die $sth->errstr; -# -# return sprintf( '%.2f', $sth->fetchrow_arrayref->[0] ); + my $custnum = $self->custnum; - my $total_bill = 0; - foreach my $cust_bill ( - grep { $_->_date <= $time } - qsearch('cust_bill', { 'custnum' => $self->custnum, } ) - ) { - $total_bill += $cust_bill->owed; - } - sprintf( "%.2f", $total_bill ); + my $owed_sql = FS::cust_bill->owed_sql; + + my $sql = " + SELECT SUM($owed_sql) FROM cust_bill + WHERE custnum = $custnum + AND _date <= $time + "; + + sprintf( "%.2f", $self->scalar_sql($sql) ); } @@ -5925,9 +5951,18 @@ sub total_credited { sub total_unapplied_credits { my $self = shift; - my $total_credit = 0; - $total_credit += $_->credited foreach $self->cust_credit; - sprintf( "%.2f", $total_credit ); + + my $custnum = $self->custnum; + + my $unapplied_sql = FS::cust_credit->unapplied_sql; + + my $sql = " + SELECT SUM($unapplied_sql) FROM cust_credit + WHERE custnum = $custnum + "; + + sprintf( "%.2f", $self->scalar_sql($sql) ); + } =item total_unapplied_credits_pkgnum PKGNUM @@ -5954,9 +5989,18 @@ See L. sub total_unapplied_payments { my $self = shift; - my $total_unapplied = 0; - $total_unapplied += $_->unapplied foreach $self->cust_pay; - sprintf( "%.2f", $total_unapplied ); + + my $custnum = $self->custnum; + + my $unapplied_sql = FS::cust_pay->unapplied_sql; + + my $sql = " + SELECT SUM($unapplied_sql) FROM cust_pay + WHERE custnum = $custnum + "; + + sprintf( "%.2f", $self->scalar_sql($sql) ); + } =item total_unapplied_payments_pkgnum PKGNUM @@ -5984,9 +6028,17 @@ customer. See L. sub total_unapplied_refunds { my $self = shift; - my $total_unapplied = 0; - $total_unapplied += $_->unapplied foreach $self->cust_refund; - sprintf( "%.2f", $total_unapplied ); + my $custnum = $self->custnum; + + my $unapplied_sql = FS::cust_refund->unapplied_sql; + + my $sql = " + SELECT SUM($unapplied_sql) FROM cust_refund + WHERE custnum = $custnum + "; + + sprintf( "%.2f", $self->scalar_sql($sql) ); + } =item balance @@ -5998,12 +6050,7 @@ total_unapplied_credits minus total_unapplied_payments). sub balance { my $self = shift; - sprintf( "%.2f", - $self->total_owed - + $self->total_unapplied_refunds - - $self->total_unapplied_credits - - $self->total_unapplied_payments - ); + $self->balance_date_range; } =item balance_date TIME @@ -6018,19 +6065,13 @@ functions. sub balance_date { my $self = shift; - my $time = shift; - sprintf( "%.2f", - $self->total_owed_date($time) - + $self->total_unapplied_refunds - - $self->total_unapplied_credits - - $self->total_unapplied_payments - ); + $self->balance_date_range(shift); } -=item balance_date_range START_TIME [ END_TIME [ OPTION => VALUE ... ] ] +=item balance_date_range [ START_TIME [ END_TIME [ OPTION => VALUE ... ] ] ] -Returns the balance for this customer, only considering invoices with date -earlier than START_TIME, and optionally not later than END_TIME +Returns the balance for this customer, optionally considering invoices with +date earlier than START_TIME, and not later than END_TIME (total_owed_date minus total_unapplied_credits minus total_unapplied_payments). Times are specified as SQL fragments or numeric @@ -7357,10 +7398,10 @@ sub balance_sql { " WHERE cust_refund.custnum = cust_main.custnum ) "; } -=item balance_date_sql START_TIME [ END_TIME [ OPTION => VALUE ... ] ] +=item balance_date_sql [ START_TIME [ END_TIME [ OPTION => VALUE ... ] ] ] -Returns an SQL fragment to retreive the balance for this customer, only -considering invoices with date earlier than START_TIME, and optionally not +Returns an SQL fragment to retreive the balance for this customer, optionally +considering invoices with date earlier than START_TIME, and not later than END_TIME (total_owed_date minus total_unapplied_credits minus total_unapplied_payments). @@ -7392,6 +7433,12 @@ WHERE clause hashref (elements "AND"ed together) (typically used with the total (unused. obsolete?) JOIN clause (typically used with the total option) +=item cutoff + +An absolute cutoff time. Payments, credits, and refunds I after this +time will be ignored. Note that START_TIME and END_TIME only limit the date +range for invoices and I payments, credits, and refunds. + =back =cut @@ -7399,10 +7446,12 @@ JOIN clause (typically used with the total option) sub balance_date_sql { my( $class, $start, $end, %opt ) = @_; - my $owed = FS::cust_bill->owed_sql; - my $unapp_refund = FS::cust_refund->unapplied_sql; - my $unapp_credit = FS::cust_credit->unapplied_sql; - my $unapp_pay = FS::cust_pay->unapplied_sql; + my $cutoff = $opt{'cutoff'}; + + my $owed = FS::cust_bill->owed_sql($cutoff); + my $unapp_refund = FS::cust_refund->unapplied_sql($cutoff); + my $unapp_credit = FS::cust_credit->unapplied_sql($cutoff); + my $unapp_pay = FS::cust_pay->unapplied_sql($cutoff); my $j = $opt{'join'} || ''; @@ -7435,9 +7484,11 @@ Available options are: =cut sub unapplied_payments_date_sql { - my( $class, $start, $end, ) = @_; + my( $class, $start, $end, %opt ) = @_; + + my $cutoff = $opt{'cutoff'}; - my $unapp_pay = FS::cust_pay->unapplied_sql; + my $unapp_pay = FS::cust_pay->unapplied_sql($cutoff); my $pay_where = $class->_money_table_where( 'cust_pay', $start, $end, 'unapplied_date'=>1 ); @@ -8790,14 +8841,23 @@ sub _agent_plandata { } +=item queued_bill 'custnum' => CUSTNUM [ , OPTION => VALUE ... ] + +Subroutine (not a method), designed to be called from the queue. + +Takes a list of options and values. + +Pulls up the customer record via the custnum option and calls bill_and_collect. + +=cut + sub queued_bill { - ## actual sub, not a method, designed to be called from the queue. - ## sets up the customer, and calls the bill_and_collect my (%args) = @_; #, ($time, $invoice_time, $check_freq, $resetup) = @_; + my $cust_main = qsearchs( 'cust_main', { custnum => $args{'custnum'} } ); - $cust_main->bill_and_collect( - %args, - ); + warn 'bill_and_collect custnum#'. $cust_main->custnum. "\n";#log custnum w/pid + + $cust_main->bill_and_collect( %args ); } sub _upgrade_data { #class method