X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=FS%2FFS%2Fcust_main.pm;h=c1a8aafdee9dbde3456de3d202d0f47c20865baf;hb=7b125e587a4d1ee0aca692e23ea7897f671855ae;hp=d585b0dfa0332c88cdffe8c57ad1d603a9c57d43;hpb=98b9624b75d092f9451132182bf17fdf24ce405e;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index d585b0dfa..c1a8aafde 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -62,7 +62,6 @@ use FS::queue; use FS::part_pkg; use FS::part_event; use FS::part_event_condition; -use FS::part_export; #use FS::cust_event; use FS::type_pkgs; use FS::payment_gateway; @@ -87,7 +86,7 @@ $skip_fuzzyfiles = 0; @fuzzyfields = ( 'first', 'last', 'company', 'address1' ); @encrypted_fields = ('payinfo', 'paycvv'); -sub nohistory_fields { ('payinfo', 'paycvv'); } +sub nohistory_fields { ('paycvv'); } @paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings'); @@ -547,45 +546,6 @@ sub insert { } } - # cust_main exports! - warn " exporting\n" if $DEBUG > 1; - - my $export_args = $options{'export_args'} || []; - - my @part_export = - map qsearch( 'part_export', {exportnum=>$_} ), - $conf->config('cust_main-exports'); #, $agentnum - - foreach my $part_export ( @part_export ) { - my $error = $part_export->export_insert($self, @$export_args); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "exporting to ". $part_export->exporttype. - " (transaction rolled back): $error"; - } - } - - #foreach my $depend_jobnum ( @$depend_jobnums ) { - # warn "[$me] inserting dependancies on supplied job $depend_jobnum\n" - # if $DEBUG; - # foreach my $jobnum ( @jobnums ) { - # my $queue = qsearchs('queue', { 'jobnum' => $jobnum } ); - # warn "[$me] inserting dependancy for job $jobnum on $depend_jobnum\n" - # if $DEBUG; - # my $error = $queue->depend_insert($depend_jobnum); - # if ( $error ) { - # $dbh->rollback if $oldAutoCommit; - # return "error queuing job dependancy: $error"; - # } - # } - # } - # - #} - # - #if ( exists $options{'jobnums'} ) { - # push @{ $options{'jobnums'} }, @jobnums; - #} - warn " insert complete; committing transaction\n" if $DEBUG > 1; @@ -1380,23 +1340,6 @@ sub delete { return $error; } - # cust_main exports! - - #my $export_args = $options{'export_args'} || []; - - my @part_export = - map qsearch( 'part_export', {exportnum=>$_} ), - $conf->config('cust_main-exports'); #, $agentnum - - foreach my $part_export ( @part_export ) { - my $error = $part_export->export_delete( $self ); #, @$export_args); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "exporting to ". $part_export->exporttype. - " (transaction rolled back): $error"; - } - } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -1510,15 +1453,8 @@ sub replace { } - 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) - ) - ) - { - + if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ && + grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) { # card/check/lec info has changed, want to retry realtime_ invoice events my $error = $self->retry_realtime; if ( $error ) { @@ -1535,23 +1471,6 @@ sub replace { } } - # cust_main exports! - - my $export_args = $options{'export_args'} || []; - - my @part_export = - map qsearch( 'part_export', {exportnum=>$_} ), - $conf->config('cust_main-exports'); #, $agentnum - - foreach my $part_export ( @part_export ) { - my $error = $part_export->export_replace( $self, $old, @$export_args); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "exporting to ". $part_export->exporttype. - " (transaction rolled back): $error"; - } - } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -1789,7 +1708,12 @@ sub check { # If it is encrypted and the private key is not availaible then we can't # check the credit card. - my $check_payinfo = ! $self->is_encrypted($self->payinfo); + + my $check_payinfo = 1; + + if ($self->is_encrypted($self->payinfo)) { + $check_payinfo = 0; + } if ( $check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) { @@ -1803,8 +1727,7 @@ sub check { or return gettext('invalid_card'); # . ": ". $self->payinfo; return gettext('unknown_card_type') - if $self->payinfo !~ /^99\d{14}$/ #token - && cardtype($self->payinfo) eq "Unknown"; + if cardtype($self->payinfo) eq "Unknown"; my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref); if ( $ban ) { @@ -2181,9 +2104,6 @@ sub sort_packages { return 1 if !$a_num_cust_svc && $b_num_cust_svc; my @a_cust_svc = $a->cust_svc; my @b_cust_svc = $b->cust_svc; - return 0 if !scalar(@a_cust_svc) && !scalar(@b_cust_svc); - return -1 if scalar(@a_cust_svc) && !scalar(@b_cust_svc); - return 1 if !scalar(@a_cust_svc) && scalar(@b_cust_svc); $a_cust_svc[0]->svc_x->label cmp $b_cust_svc[0]->svc_x->label; } @@ -2761,14 +2681,8 @@ sub bill { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - warn "$me acquiring lock on customer ". $self->custnum. "\n" - if $DEBUG; - $self->select_for_update; #mutex - warn "$me running pre-bill events for customer ". $self->custnum. "\n" - if $DEBUG; - my $error = $self->do_cust_event( 'debug' => ( $options{'debug'} || 0 ), 'time' => $invoice_time, @@ -2780,9 +2694,6 @@ sub bill { return $error; } - warn "$me done running pre-bill events for customer ". $self->custnum. "\n" - if $DEBUG; - #keep auto-charge and non-auto-charge line items separate my @passes = ( '', 'no_auto' ); @@ -3708,17 +3619,19 @@ sub collect { } } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; - - #never want to roll back an event just because it returned an error - local $FS::UID::AutoCommit = 1; #$oldAutoCommit; - - $self->do_cust_event( + my $error = $self->do_cust_event( 'debug' => ( $options{'debug'} || 0 ), 'time' => $invoice_time, 'check_freq' => $options{'check_freq'}, 'stage' => 'collect', ); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; } @@ -3813,11 +3726,6 @@ sub do_cust_event { return $due_cust_event; } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; - #never want to roll back an event just because it or a different one - # returned an error - local $FS::UID::AutoCommit = 1; #$oldAutoCommit; - foreach my $cust_event ( @$due_cust_event ) { #XXX lock event @@ -3826,7 +3734,11 @@ sub do_cust_event { unless ( $cust_event->test_conditions( 'time' => $time ) ) { #don't leave stray "new/locked" records around my $error = $cust_event->delete; - return $error if $error; + if ( $error ) { + #gah, even with transactions + $dbh->commit if $oldAutoCommit; #well. + return $error; + } next; } @@ -3835,16 +3747,20 @@ sub do_cust_event { warn " running cust_event ". $cust_event->eventnum. "\n" if $DEBUG > 1; + #if ( my $error = $cust_event->do_event(%options) ) { #XXX %options? if ( my $error = $cust_event->do_event() ) { #XXX wtf is this? figure out a proper dealio with return value #from do_event - return $error; - } + # gah, even with transactions. + $dbh->commit if $oldAutoCommit; #well. + return $error; + } } } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; } @@ -4323,7 +4239,6 @@ sub _bop_options { $options->{payment_gateway}->gatewaynum ? $options->{payment_gateway}->options : @{ $options->{payment_gateway}->get('options') }; - } sub _bop_defaults { @@ -4350,6 +4265,14 @@ 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); @@ -4360,30 +4283,14 @@ sub _bop_content { ( $conf->exists('business-onlinepayment-email_customer') || $conf->exists('business-onlinepayment-email-override') ); - 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{payfirst} = $self->getfield('first'); + $content{paylast} = $self->getfield('last'); - $content{last_name} = $paylast; - $content{first_name} = $payfirst; + $content{account_name} = "$content{payfirst} $content{paylast}" + if $options->{method} eq 'ECHECK'; - $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{name} = $options->{payname}; + $content{name} = $content{account_name} if exists($content{account_name}); $content{city} = exists($options->{city}) ? $options->{city} @@ -4397,11 +4304,10 @@ 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 = ( @@ -4477,8 +4383,13 @@ sub realtime_bop { # massage data ### - my $bop_content = $self->_bop_content(\%options); - return $bop_content unless ref($bop_content); + 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 @invoicing_list = $self->invoicing_list_emailonly; if ( $conf->exists('emailinvoiceautoalways') @@ -4544,9 +4455,6 @@ 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'} @@ -4631,7 +4539,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 @@ -4646,8 +4554,6 @@ 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 ) { @@ -4700,8 +4606,6 @@ sub realtime_bop { $capture->content( %capture ); - $capture->test_transaction(1) - if $conf->exists('business-onlinepayment-test_transaction'); $capture->submit(); unless ( $capture->is_success ) { @@ -4731,25 +4635,6 @@ 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 ### @@ -4872,7 +4757,7 @@ sub _realtime_bop_result { 'paid' => $cust_pay_pending->paid, '_date' => '', 'payby' => $cust_pay_pending->payby, - 'payinfo' => $options{'payinfo'}, + #'payinfo' => $payinfo, 'paybatch' => $paybatch, 'paydate' => $cust_pay_pending->paydate, 'pkgnum' => $cust_pay_pending->pkgnum, @@ -5276,7 +5161,7 @@ sub realtime_refund_bop { my $self = shift; my %options = (); - if (ref($_[0]) eq 'HASH') { + if (ref($_[0]) ne 'HASH') { %options = %{$_[0]}; } else { my $method = shift; @@ -5408,8 +5293,6 @@ 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'}); @@ -5512,8 +5395,6 @@ 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 @@ -5952,17 +5833,29 @@ 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 $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] ); - sprintf( "%.2f", $self->scalar_sql($sql) ); + 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 ); } @@ -6032,18 +5925,9 @@ sub total_credited { sub total_unapplied_credits { my $self = shift; - - 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) ); - + my $total_credit = 0; + $total_credit += $_->credited foreach $self->cust_credit; + sprintf( "%.2f", $total_credit ); } =item total_unapplied_credits_pkgnum PKGNUM @@ -6070,18 +5954,9 @@ See L. sub total_unapplied_payments { my $self = shift; - - 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) ); - + my $total_unapplied = 0; + $total_unapplied += $_->unapplied foreach $self->cust_pay; + sprintf( "%.2f", $total_unapplied ); } =item total_unapplied_payments_pkgnum PKGNUM @@ -6109,17 +5984,9 @@ customer. See L. sub total_unapplied_refunds { my $self = shift; - 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) ); - + my $total_unapplied = 0; + $total_unapplied += $_->unapplied foreach $self->cust_refund; + sprintf( "%.2f", $total_unapplied ); } =item balance @@ -6131,7 +5998,12 @@ total_unapplied_credits minus total_unapplied_payments). sub balance { my $self = shift; - $self->balance_date_range; + sprintf( "%.2f", + $self->total_owed + + $self->total_unapplied_refunds + - $self->total_unapplied_credits + - $self->total_unapplied_payments + ); } =item balance_date TIME @@ -6146,13 +6018,19 @@ functions. sub balance_date { my $self = shift; - $self->balance_date_range(shift); + my $time = shift; + sprintf( "%.2f", + $self->total_owed_date($time) + + $self->total_unapplied_refunds + - $self->total_unapplied_credits + - $self->total_unapplied_payments + ); } -=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, optionally considering invoices with -date earlier than START_TIME, and not later than END_TIME +Returns the balance for this customer, only considering invoices with date +earlier than START_TIME, and optionally not later than END_TIME (total_owed_date minus total_unapplied_credits minus total_unapplied_payments). Times are specified as SQL fragments or numeric @@ -7216,8 +7094,6 @@ Returns a status string for this customer, currently: =item prospect - No packages have ever been ordered -=item ordered - Recurring packages all are new (not yet billed). - =item active - One or more recurring packages is active =item inactive - No active recurring packages, but otherwise unsuspended/uncancelled (the inactive status is new - previously inactive customers were mis-identified as cancelled) @@ -7234,8 +7110,7 @@ sub status { shift->cust_status(@_); } sub cust_status { my $self = shift; - # prospect ordered active inactive suspended cancelled - for my $status ( FS::cust_main->statuses() ) { + for my $status (qw( prospect active inactive suspended cancelled )) { my $method = $status.'_sql'; my $numnum = ( my $sql = $self->$method() ) =~ s/cust_main\.custnum/?/g; my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr; @@ -7270,7 +7145,6 @@ use vars qw(%statuscolor); tie %statuscolor, 'Tie::IxHash', 'prospect' => '7e0079', #'000000', #black? naw, purple 'active' => '00CC00', #green - 'ordered' => '009999', #teal? cyan? 'inactive' => '0000CC', #blue 'suspended' => 'FF9900', #yellow 'cancelled' => 'FF0000', #red @@ -7380,20 +7254,9 @@ sub select_count_pkgs_sql { $select_count_pkgs; } -sub prospect_sql { - " 0 = ( $select_count_pkgs ) "; -} - -=item ordered_sql - -Returns an SQL expression identifying ordered cust_main records (customers with -recurring packages not yet setup). - -=cut - -sub ordered_sql { - " 0 < ( $select_count_pkgs AND ". FS::cust_pkg->ordered_sql. " ) "; -} +sub prospect_sql { " + 0 = ( $select_count_pkgs ) +"; } =item active_sql @@ -7402,9 +7265,10 @@ active recurring packages). =cut -sub active_sql { - " 0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " ) "; -} +sub active_sql { " + 0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " + ) +"; } =item inactive_sql @@ -7493,10 +7357,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, optionally -considering invoices with date earlier than START_TIME, and not +Returns an SQL fragment to retreive the balance for this customer, only +considering invoices with date earlier than START_TIME, and optionally not later than END_TIME (total_owed_date minus total_unapplied_credits minus total_unapplied_payments). @@ -7579,11 +7443,9 @@ Available options are: =cut sub unapplied_payments_date_sql { - my( $class, $start, $end, %opt ) = @_; + my( $class, $start, $end, ) = @_; - my $cutoff = $opt{'cutoff'}; - - my $unapp_pay = FS::cust_pay->unapplied_sql($cutoff); + my $unapp_pay = FS::cust_pay->unapplied_sql; my $pay_where = $class->_money_table_where( 'cust_pay', $start, $end, 'unapplied_date'=>1 ); @@ -7694,7 +7556,7 @@ sub search { # parse status ## - #prospect ordered active inactive suspended cancelled + #prospect active inactive suspended cancelled if ( grep { $params->{'status'} eq $_ } FS::cust_main->statuses() ) { my $method = $params->{'status'}. '_sql'; #push @where, $class->$method(); @@ -8936,23 +8798,14 @@ 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'} } ); - warn 'bill_and_collect custnum#'. $cust_main->custnum. "\n";#log custnum w/pid - - $cust_main->bill_and_collect( %args ); + $cust_main->bill_and_collect( + %args, + ); } sub _upgrade_data { #class method