X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=1d220fecd3f45055c5760ff08388a6e8e74cbafc;hb=2946a850409f34e56fc484e940a2f938101fb60e;hp=6c2bcf377c075bfaf2021a21a5e3c43e5faab803;hpb=f433b4f187e484e8c40bdcebbeb5a5d51b3ae38e;p=freeside.git diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 6c2bcf377..1d220fecd 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2,7 +2,8 @@ package FS::cust_main; require 5.006; use strict; -use vars qw( @ISA @EXPORT_OK $DEBUG $me $conf +use base qw( FS::otaker_Mixin FS::payinfo_Mixin FS::Record ); +use vars qw( @EXPORT_OK $DEBUG $me $conf @encrypted_fields $import $ignore_expired_card $skip_fuzzyfiles @fuzzyfields @@ -25,7 +26,7 @@ use String::Approx qw(amatch); use Business::CreditCard 0.28; use Locale::Country; use FS::UID qw( getotaker dbh driver_name ); -use FS::Record qw( qsearchs qsearch dbdef ); +use FS::Record qw( qsearchs qsearch dbdef regexp_sql ); use FS::Misc qw( generate_email send_email generate_ps do_print ); use FS::Msgcat qw(gettext); use FS::payby; @@ -66,11 +67,8 @@ use FS::type_pkgs; use FS::payment_gateway; use FS::agent_payment_gateway; use FS::banned_pay; -use FS::payinfo_Mixin; use FS::TicketSystem; -@ISA = qw( FS::payinfo_Mixin FS::Record ); - @EXPORT_OK = qw( smart_search ); $realtime_bop_decline_quiet = 0; @@ -304,9 +302,9 @@ IP address from which payment information was received Tax exempt, empty or `Y' -=item otaker +=item usernum -Order taker (assigned automatically, see L) +Order taker (see L) =item comments @@ -2696,15 +2694,21 @@ sub bill { return $error; } - my @cust_bill_pkg = (); + #keep auto-charge and non-auto-charge line items separate + my @passes = ( '', 'no_auto' ); + + my %cust_bill_pkg = map { $_ => [] } @passes; ### # find the packages which are due for billing, find out how much they are # & generate invoice database. ### - my( $total_setup, $total_recur, $postal_charge ) = ( 0, 0, 0 ); - my %taxlisthash; + my %total_setup = map { my $z = 0; $_ => \$z; } @passes; + my %total_recur = map { my $z = 0; $_ => \$z; } @passes; + + my %taxlisthash = map { $_ => {} } @passes; + my @precommit_hooks = (); $options{'pkg_list'} ||= [ $self->ncancelled_pkgs ]; #param checks? @@ -2727,14 +2731,16 @@ sub bill { $cust_pkg->set($_, $hash{$_}) foreach qw ( setup last_bill bill ); + my $pass = ($cust_pkg->no_auto || $part_pkg->no_auto) ? 'no_auto' : ''; + my $error = $self->_make_lines( 'part_pkg' => $part_pkg, 'cust_pkg' => $cust_pkg, 'precommit_hooks' => \@precommit_hooks, - 'line_items' => \@cust_bill_pkg, - 'setup' => \$total_setup, - 'recur' => \$total_recur, - 'tax_matrix' => \%taxlisthash, + 'line_items' => $cust_bill_pkg{$pass}, + 'setup' => $total_setup{$pass}, + 'recur' => $total_recur{$pass}, + 'tax_matrix' => $taxlisthash{$pass}, 'time' => $time, 'real_pkgpart' => $real_pkgpart, 'options' => \%options, @@ -2748,130 +2754,138 @@ sub bill { } #foreach my $cust_pkg - unless ( @cust_bill_pkg ) { #don't create an invoice w/o line items - #but do commit any package date cycling that happened - $dbh->commit or die $dbh->errstr if $oldAutoCommit; - return ''; - } + #if the customer isn't on an automatic payby, everything can go on a single + #invoice anyway? + #if ( $cust_main->payby !~ /^(CARD|CHEK)$/ ) { + #merge everything into one list + #} - if ( scalar( grep { $_->recur && $_->recur > 0 } @cust_bill_pkg) || - !$conf->exists('postal_invoice-recurring_only') - ) - { + foreach my $pass (@passes) { # keys %cust_bill_pkg ) { - my $postal_pkg = $self->charge_postal_fee(); - if ( $postal_pkg && !ref( $postal_pkg ) ) { + my @cust_bill_pkg = @{ $cust_bill_pkg{$pass} }; - $dbh->rollback if $oldAutoCommit; - return "can't charge postal invoice fee for customer ". - $self->custnum. ": $postal_pkg"; - - } elsif ( $postal_pkg ) { - - my $real_pkgpart = $postal_pkg->pkgpart; - foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) { - my %postal_options = %options; - delete $postal_options{cancel}; - my $error = - $self->_make_lines( 'part_pkg' => $part_pkg, - 'cust_pkg' => $postal_pkg, - 'precommit_hooks' => \@precommit_hooks, - 'line_items' => \@cust_bill_pkg, - 'setup' => \$total_setup, - 'recur' => \$total_recur, - 'tax_matrix' => \%taxlisthash, - 'time' => $time, - 'real_pkgpart' => $real_pkgpart, - 'options' => \%postal_options, - ); - if ($error) { - $dbh->rollback if $oldAutoCommit; - return $error; - } - } + next unless @cust_bill_pkg; #don't create an invoice w/o line items - } + if ( scalar( grep { $_->recur && $_->recur > 0 } @cust_bill_pkg) || + !$conf->exists('postal_invoice-recurring_only') + ) + { - } + my $postal_pkg = $self->charge_postal_fee(); + if ( $postal_pkg && !ref( $postal_pkg ) ) { - my $listref_or_error = - $self->calculate_taxes( \@cust_bill_pkg, \%taxlisthash, $invoice_time); + $dbh->rollback if $oldAutoCommit; + return "can't charge postal invoice fee for customer ". + $self->custnum. ": $postal_pkg"; + + } elsif ( $postal_pkg ) { + + my $real_pkgpart = $postal_pkg->pkgpart; + foreach my $part_pkg ( $postal_pkg->part_pkg->self_and_bill_linked ) { + my %postal_options = %options; + delete $postal_options{cancel}; + my $error = + $self->_make_lines( 'part_pkg' => $part_pkg, + 'cust_pkg' => $postal_pkg, + 'precommit_hooks' => \@precommit_hooks, + 'line_items' => \@cust_bill_pkg, + 'setup' => $total_setup{$pass}, + 'recur' => $total_recur{$pass}, + 'tax_matrix' => $taxlisthash{$pass}, + 'time' => $time, + 'real_pkgpart' => $real_pkgpart, + 'options' => \%postal_options, + ); + if ($error) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } - unless ( ref( $listref_or_error ) ) { - $dbh->rollback if $oldAutoCommit; - return $listref_or_error; - } + } - foreach my $taxline ( @$listref_or_error ) { - $total_setup = sprintf('%.2f', $total_setup+$taxline->setup ); - push @cust_bill_pkg, $taxline; - } + } - #add tax adjustments - warn "adding tax adjustments...\n" if $DEBUG > 2; - foreach my $cust_tax_adjustment ( - qsearch('cust_tax_adjustment', { 'custnum' => $self->custnum, - 'billpkgnum' => '', - } - ) - ) { + my $listref_or_error = + $self->calculate_taxes( \@cust_bill_pkg, $taxlisthash{$pass}, $invoice_time); - my $tax = sprintf('%.2f', $cust_tax_adjustment->amount ); - - my $itemdesc = $cust_tax_adjustment->taxname; - $itemdesc = '' if $itemdesc eq 'Tax'; - - push @cust_bill_pkg, new FS::cust_bill_pkg { - 'pkgnum' => 0, - 'setup' => $tax, - 'recur' => 0, - 'sdate' => '', - 'edate' => '', - 'itemdesc' => $itemdesc, - 'itemcomment' => $cust_tax_adjustment->comment, - 'cust_tax_adjustment' => $cust_tax_adjustment, - #'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location, - }; + unless ( ref( $listref_or_error ) ) { + $dbh->rollback if $oldAutoCommit; + return $listref_or_error; + } - } + foreach my $taxline ( @$listref_or_error ) { + ${ $total_setup{$pass} } = + sprintf('%.2f', ${ $total_setup{$pass} } + $taxline->setup ); + push @cust_bill_pkg, $taxline; + } - my $charged = sprintf('%.2f', $total_setup + $total_recur ); + #add tax adjustments + warn "adding tax adjustments...\n" if $DEBUG > 2; + foreach my $cust_tax_adjustment ( + qsearch('cust_tax_adjustment', { 'custnum' => $self->custnum, + 'billpkgnum' => '', + } + ) + ) { - my @cust_bill = $self->cust_bill; - my $balance = $self->balance; - my $previous_balance = scalar(@cust_bill) - ? ( $cust_bill[$#cust_bill]->billing_balance || 0 ) - : 0; + my $tax = sprintf('%.2f', $cust_tax_adjustment->amount ); + + my $itemdesc = $cust_tax_adjustment->taxname; + $itemdesc = '' if $itemdesc eq 'Tax'; + + push @cust_bill_pkg, new FS::cust_bill_pkg { + 'pkgnum' => 0, + 'setup' => $tax, + 'recur' => 0, + 'sdate' => '', + 'edate' => '', + 'itemdesc' => $itemdesc, + 'itemcomment' => $cust_tax_adjustment->comment, + 'cust_tax_adjustment' => $cust_tax_adjustment, + #'cust_bill_pkg_tax_location' => \@cust_bill_pkg_tax_location, + }; - $previous_balance += $cust_bill[$#cust_bill]->charged - if scalar(@cust_bill); - #my $balance_adjustments = - # sprintf('%.2f', $balance - $prior_prior_balance - $prior_charged); + } - #create the new invoice - my $cust_bill = new FS::cust_bill ( { - 'custnum' => $self->custnum, - '_date' => ( $invoice_time ), - 'charged' => $charged, - 'billing_balance' => $balance, - 'previous_balance' => $previous_balance, - 'invoice_terms' => $options{'invoice_terms'}, - } ); - $error = $cust_bill->insert; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "can't create invoice for customer #". $self->custnum. ": $error"; - } + my $charged = sprintf('%.2f', ${ $total_setup{$pass} } + ${ $total_recur{$pass} } ); - foreach my $cust_bill_pkg ( @cust_bill_pkg ) { - $cust_bill_pkg->invnum($cust_bill->invnum); - my $error = $cust_bill_pkg->insert; + my @cust_bill = $self->cust_bill; + my $balance = $self->balance; + my $previous_balance = scalar(@cust_bill) + ? ( $cust_bill[$#cust_bill]->billing_balance || 0 ) + : 0; + + $previous_balance += $cust_bill[$#cust_bill]->charged + if scalar(@cust_bill); + #my $balance_adjustments = + # sprintf('%.2f', $balance - $prior_prior_balance - $prior_charged); + + #create the new invoice + my $cust_bill = new FS::cust_bill ( { + 'custnum' => $self->custnum, + '_date' => ( $invoice_time ), + 'charged' => $charged, + 'billing_balance' => $balance, + 'previous_balance' => $previous_balance, + 'invoice_terms' => $options{'invoice_terms'}, + } ); + $error = $cust_bill->insert; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "can't create invoice line item: $error"; + return "can't create invoice for customer #". $self->custnum. ": $error"; } - } - + + foreach my $cust_bill_pkg ( @cust_bill_pkg ) { + $cust_bill_pkg->invnum($cust_bill->invnum); + my $error = $cust_bill_pkg->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "can't create invoice line item: $error"; + } + } + + } #foreach my $pass ( keys %cust_bill_pkg ) foreach my $hook ( @precommit_hooks ) { eval { @@ -7433,7 +7447,7 @@ sub referral_cust_main_ncancelled { Like referral_cust_main, except returns a flat list of all unsuspended (and uncancelled) packages for each customer. The number of items in this list may -be useful for comission calculations (perhaps after a Cpkgpart; grep { $_ == $pkgpart } @commission_worthy_pkgparts> } $cust_main-> ). +be useful for commission calculations (perhaps after a Cpkgpart; grep { $_ == $pkgpart } @commission_worthy_pkgparts> } $cust_main-> ). =cut @@ -7495,8 +7509,10 @@ sub credit { $cust_credit->set('reason', $reason) } - $cust_credit->addlinfo( delete $options{'addlinfo'} ) - if exists($options{'addlinfo'}); + for (qw( addlinfo eventnum )) { + $cust_credit->$_( delete $options{$_} ) + if exists($options{$_}); + } $cust_credit->insert(%options); @@ -7549,12 +7565,14 @@ sub charge { my ( $pkg, $comment, $additional ); my ( $setuptax, $taxclass ); #internal taxes my ( $taxproduct, $override ); #vendor (CCH) taxes + my $no_auto = ''; my $cust_pkg_ref = ''; my ( $bill_now, $invoice_terms ) = ( 0, '' ); if ( ref( $_[0] ) ) { $amount = $_[0]->{amount}; $quantity = exists($_[0]->{quantity}) ? $_[0]->{quantity} : 1; $start_date = exists($_[0]->{start_date}) ? $_[0]->{start_date} : ''; + $no_auto = exists($_[0]->{no_auto}) ? $_[0]->{no_auto} : ''; $pkg = exists($_[0]->{pkg}) ? $_[0]->{pkg} : 'One-time charge'; $comment = exists($_[0]->{comment}) ? $_[0]->{comment} : '$'. sprintf("%.2f",$amount); @@ -7632,6 +7650,7 @@ sub charge { 'pkgpart' => $pkgpart, 'quantity' => $quantity, 'start_date' => $start_date, + 'no_auto' => $no_auto, } ); $error = $cust_pkg->insert; @@ -8505,6 +8524,15 @@ sub search { } ## + # do the same for user + ## + + if ( $params->{'usernum'} =~ /^(\d+)$/ and $1 ) { + push @where, + "cust_main.usernum = $1"; + } + + ## # parse status ## @@ -8539,13 +8567,23 @@ sub search { next unless exists($params->{$field}); - my($beginning, $ending) = @{$params->{$field}}; + my($beginning, $ending, $hour) = @{$params->{$field}}; push @where, "cust_main.$field IS NOT NULL", "cust_main.$field >= $beginning", "cust_main.$field <= $ending"; + # XXX: do this for mysql and/or pull it out of here + if(defined $hour) { + if ($dbh->{Driver}->{Name} eq 'Pg') { + push @where, "extract(hour from to_timestamp(cust_main.$field)) = $hour"; + } + else { + warn "search by time of day not supported on ".$dbh->{Driver}->{Name}." databases"; + } + } + $orderby ||= "ORDER BY cust_main.$field"; } @@ -9691,14 +9729,7 @@ sub _agent_plandata { my $agentnum = $self->agentnum; - my $regexp = ''; - if ( driver_name =~ /^Pg/i ) { - $regexp = '~'; - } elsif ( driver_name =~ /^mysql/i ) { - $regexp = 'REGEXP'; - } else { - die "don't know how to use regular expressions in ". driver_name. " databases"; - } + my $regexp = regexp_sql(); my $part_event_option = qsearchs({ @@ -9764,6 +9795,8 @@ sub _upgrade_data { #class method my $sth = dbh->prepare($sql) or die dbh->errstr; $sth->execute or die $sth->errstr; + $class->_upgrade_otaker(%opts); + } =back