X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main.pm;h=a9fcb2b6610f43867393ab32cf9e671b778657e7;hp=2c89bb065d9879b2c6142ece7d6432900c8fff4e;hb=c65b166b6e2ebdac5c2eb2e8336ebd1a4087f77c;hpb=c44e20f5c73abae61a15a415b4393ecd38297c65 diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 2c89bb065..a9fcb2b66 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -1,7 +1,7 @@ package FS::cust_main; use strict; -use vars qw( @ISA $conf $Debug $import ); +use vars qw( @ISA $conf $DEBUG $import ); use vars qw( $realtime_bop_decline_quiet ); #ugh use Safe; use Carp; @@ -22,6 +22,7 @@ use FS::cust_bill; use FS::cust_bill_pkg; use FS::cust_pay; use FS::cust_credit; +use FS::cust_refund; use FS::part_referral; use FS::cust_main_county; use FS::agent; @@ -41,8 +42,8 @@ use FS::Msgcat qw(gettext); $realtime_bop_decline_quiet = 0; -$Debug = 1; -#$Debug = 1; +$DEBUG = 0; +#$DEBUG = 1; $import = 0; @@ -231,10 +232,16 @@ invoicing_list destination to the newly-created svc_acct. Here's an example: $cust_main->insert( {}, [ $email, 'POST' ] ); -Currently available options are: I +Currently available options are: I and I. -If I is set true, no provisioning jobs (exports) are scheduled. -(You can schedule them later with the B method.) +If I is set, all provisioning jobs will have a dependancy +on the supplied jobnum (they will not run until the specific job completes). +This can be used to defer provisioning until some action completes (such +as running the customer's credit card sucessfully). + +The I option is deprecated. If I is set true, no +provisioning jobs (exports) are scheduled. (You can schedule them later with +the B method.) =cut @@ -243,6 +250,9 @@ sub insert { my $cust_pkgs = @_ ? shift : {}; my $invoicing_list = @_ ? shift : ''; my %options = @_; + warn "FS::cust_main::insert called with options ". + join(', ', map { "$_: $options{$_}" } keys %options ). "\n" + if $DEBUG; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -294,8 +304,7 @@ sub insert { } # packages - local $FS::svc_Common::noexport_hack = 1 if $options{'noexport'}; - $error = $self->order_pkgs($cust_pkgs, \$seconds); + $error = $self->order_pkgs($cust_pkgs, \$seconds, %options); if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -329,9 +338,33 @@ sub insert { } -=item order_pkgs +=item order_pkgs HASHREF, [ SECONDSREF, [ , OPTION => VALUE ... ] ] + +Like the insert method on an existing record, this method orders a package +and included services atomicaly. Pass a Tie::RefHash data structure to this +method containing FS::cust_pkg and FS::svc_I objects. There should +be a better explanation of this, but until then, here's an example: + + use Tie::RefHash; + tie %hash, 'Tie::RefHash'; #this part is important + %hash = ( + $cust_pkg => [ $svc_acct ], + ... + ); + $cust_main->order_pkgs( \%hash, \'0', 'noexport'=>1 ); + +Currently available options are: I and I. -document me. like ->insert(%cust_pkg) on an existing record +If I is set, all provisioning jobs will have a dependancy +on the supplied jobnum (they will not run until the specific job completes). +This can be used to defer provisioning until some action completes (such +as running the customer's credit card sucessfully). + +The I option is deprecated. If I is set true, no +provisioning jobs (exports) are scheduled. (You can schedule them later with +the B method for each cust_pkg object. Using the B method +on the cust_main object is not recommended, as existing services will also be +reexported.) =cut @@ -339,6 +372,13 @@ sub order_pkgs { my $self = shift; my $cust_pkgs = shift; my $seconds = shift; + my %options = @_; + my %svc_options = (); + $svc_options{'depend_jobnum'} = $options{'depend_jobnum'} + if exists $options{'depend_jobnum'}; + warn "FS::cust_main::order_pkgs called with options ". + join(', ', map { "$_: $options{$_}" } keys %options ). "\n" + if $DEBUG; local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -351,6 +391,8 @@ sub order_pkgs { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + local $FS::svc_Common::noexport_hack = 1 if $options{'noexport'}; + foreach my $cust_pkg ( keys %$cust_pkgs ) { $cust_pkg->custnum( $self->custnum ); my $error = $cust_pkg->insert; @@ -364,7 +406,7 @@ sub order_pkgs { $svc_something->seconds( $svc_something->seconds + $$seconds ); $$seconds = 0; } - $error = $svc_something->insert; + $error = $svc_something->insert(%svc_options); if ( $error ) { $dbh->rollback if $oldAutoCommit; #return "inserting svc_ (transaction rolled back): $error"; @@ -379,15 +421,21 @@ sub order_pkgs { =item reexport -document me. Re-schedules all exports by calling the B method -of all associated packages (see L). If there is an error, -returns the error; otherwise returns false. +This method is deprecated. See the I option to the insert and +order_pkgs methods for a better way to defer provisioning. + +Re-schedules all exports by calling the B method of all associated +packages (see L). If there is an error, returns the error; +otherwise returns false. =cut sub reexport { my $self = shift; + carp "warning: FS::cust_main::reexport is deprectated; ". + "use the depend_jobnum option to insert or order_pkgs to delay export"; + local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; local $SIG{QUIT} = 'IGNORE'; @@ -446,19 +494,19 @@ sub delete { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - if ( qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) ) { + if ( $self->cust_bill ) { $dbh->rollback if $oldAutoCommit; return "Can't delete a customer with invoices"; } - if ( qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) ) { + if ( $self->cust_credit ) { $dbh->rollback if $oldAutoCommit; return "Can't delete a customer with credits"; } - if ( qsearch( 'cust_pay', { 'custnum' => $self->custnum } ) ) { + if ( $self->cust_pay ) { $dbh->rollback if $oldAutoCommit; return "Can't delete a customer with payments"; } - if ( qsearch( 'cust_refund', { 'custnum' => $self->custnum } ) ) { + if ( $self->cust_refund ) { $dbh->rollback if $oldAutoCommit; return "Can't delete a customer with refunds"; } @@ -858,7 +906,7 @@ sub check { if !$import && ( $y<$nowy || ( $y==$nowy && $1<$nowm ) ); } - if ( $self->payname eq '' && $self->payby ne 'CHEK' && + if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ && ( ! $conf->exists('require_cardname') || $self->payby !~ /^(CARD|DCRD)$/ ) ) { @@ -994,7 +1042,7 @@ Always returns a list: an empty list on success or a list of errors. sub cancel { my $self = shift; - grep { $_->cancel(@_) } $self->ncancelled_pkgs; + grep { $_ } map { $_->cancel(@_) } $self->ncancelled_pkgs; } =item agent @@ -1179,15 +1227,15 @@ sub bill { } $setup = sprintf( "%.2f", $setup ); $recur = sprintf( "%.2f", $recur ); - if ( $setup < 0 ) { + if ( $setup < 0 && ! $conf->exists('allow_negative_charges') ) { $dbh->rollback if $oldAutoCommit; return "negative setup $setup for pkgnum ". $cust_pkg->pkgnum; } - if ( $recur < 0 ) { + if ( $recur < 0 && ! $conf->exists('allow_negative_charges') ) { $dbh->rollback if $oldAutoCommit; return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum; } - if ( $setup > 0 || $recur > 0 ) { + if ( $setup != 0 || $recur != 0 ) { my $cust_bill_pkg = new FS::cust_bill_pkg ({ 'pkgnum' => $cust_pkg->pkgnum, 'setup' => $setup, @@ -1217,6 +1265,16 @@ sub bill { } ); } + #one more try at a whole-country tax rate + unless ( @taxes ) { + @taxes = qsearch( 'cust_main_county', { + 'state' => '', + 'county' => '', + 'country' => $self->country, + 'taxclass' => '', + } ); + } + # maybe eliminate this entirely, along with all the 0% records unless ( @taxes ) { $dbh->rollback if $oldAutoCommit; @@ -1295,7 +1353,7 @@ sub bill { } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP' - } #if $setup > 0 || $recur > 0 + } #if $setup != 0 || $recur != 0 } #if $cust_pkg_mod_flag @@ -1442,7 +1500,7 @@ sub collect { my $dbh = dbh; my $balance = $self->balance; - warn "collect customer". $self->custnum. ": balance $balance" if $Debug; + warn "collect customer". $self->custnum. ": balance $balance" if $DEBUG; unless ( $balance > 0 ) { #redundant????? $dbh->rollback if $oldAutoCommit; #hmm return ''; @@ -1460,24 +1518,15 @@ sub collect { } } - foreach my $cust_bill ( $self->cust_bill ) { - - #this has to be before next's - my $amount = sprintf( "%.2f", $balance < $cust_bill->owed - ? $balance - : $cust_bill->owed - ); - $balance = sprintf( "%.2f", $balance - $amount ); - - next unless $cust_bill->owed > 0; + foreach my $cust_bill ( $self->open_cust_bill ) { # don't try to charge for the same invoice if it's already in a batch #next if qsearchs( 'cust_pay_batch', { 'invnum' => $cust_bill->invnum } ); - warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, balance $balance)" if $Debug; - - next unless $amount > 0; + last if $self->balance <= 0; + warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ")" + if $DEBUG; foreach my $part_bill_event ( sort { $a->seconds <=> $b->seconds @@ -1494,10 +1543,11 @@ sub collect { 'disabled' => '', } ) ) { - last unless $cust_bill->owed > 0; #don't run subsequent events if owed=0 + last if $cust_bill->owed <= 0 # don't run subsequent events if owed<=0 + || $self->balance <= 0; # or if balance<=0 warn "calling invoice event (". $part_bill_event->eventcode. ")\n" - if $Debug; + if $DEBUG; my $cust_main = $self; #for callback my $error; @@ -1635,7 +1685,7 @@ I can be set true to surpress email decline notices. sub realtime_bop { my( $self, $method, $amount, %options ) = @_; - if ( $Debug ) { + if ( $DEBUG ) { warn "$self $method $amount\n"; warn " $_ => $options{$_}\n" foreach keys %options; } @@ -2365,6 +2415,42 @@ sub open_cust_bill { grep { $_->owed > 0 } $self->cust_bill; } +=item cust_credit + +Returns all the credits (see L) for this customer. + +=cut + +sub cust_credit { + my $self = shift; + sort { $a->_date <=> $b->_date } + qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) +} + +=item cust_pay + +Returns all the payments (see L) for this customer. + +=cut + +sub cust_pay { + my $self = shift; + sort { $a->_date <=> $b->_date } + qsearch( 'cust_pay', { 'custnum' => $self->custnum } ) +} + +=item cust_refund + +Returns all the refunds (see L) for this customer. + +=cut + +sub cust_refund { + my $self = shift; + sort { $a->_date <=> $b->_date } + qsearch( 'cust_refund', { 'custnum' => $self->custnum } ) +} + =back =head1 SUBROUTINES