use Scalar::Util qw( blessed );
use List::Util qw( min );
use Time::Local qw(timelocal);
+use Storable qw(thaw);
+use MIME::Base64;
use Data::Dumper;
use Tie::IxHash;
use Digest::MD5 qw(md5_base64);
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;
}
}
+ # 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;
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;
'';
}
}
+ # 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;
'';
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;
}
Debugging level. Default is 0 (no debugging), or can be set to 1 (passed-in options), 2 (traces progress), 3 (more information), or 4 (include full search queries)
+=item job
+
+Optional FS::queue entry to receive status updates.
+
=back
Options are passed to the B<bill> and B<collect> methods verbatim, so all
#pre-printing invoices
$options{'actual_time'} ||= time;
+ my $job = $options{'job'};
+ $job->update_statustext('0,cleaning expired packages') if $job;
$error = $self->cancel_expired_pkgs( $options{actual_time} );
if ( $error ) {
$error = "Error expiring custnum ". $self->custnum. ": $error";
else { warn $error; }
}
+ $job->update_statustext('20,billing packages') if $job;
$error = $self->bill( %options );
if ( $error ) {
$error = "Error billing custnum ". $self->custnum. ": $error";
else { warn $error; }
}
+ $job->update_statustext('50,applying payments and credits') if $job;
$error = $self->apply_payments_and_credits;
if ( $error ) {
$error = "Error applying custnum ". $self->custnum. ": $error";
else { warn $error; }
}
+ $job->update_statustext('70,running collection events') if $job;
unless ( $conf->exists('cancelled_cust-noevents')
&& ! $self->num_ncancelled_pkgs
) {
else { warn $error; }
}
}
+ $job->update_statustext('100,finished') if $job;
'';
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,
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' );
}
}
- my $error = $self->do_cust_event(
+ $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(
'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;
- '';
}
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
unless ( $cust_event->test_conditions( 'time' => $time ) ) {
#don't leave stray "new/locked" records around
my $error = $cust_event->delete;
- if ( $error ) {
- #gah, even with transactions
- $dbh->commit if $oldAutoCommit; #well.
- return $error;
- }
+ return $error if $error;
next;
}
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
- # gah, even with transactions.
- $dbh->commit if $oldAutoCommit; #well.
- return $error;
- }
+ return $error;
+ }
}
}
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
=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)
use vars qw(%statuscolor);
tie %statuscolor, 'Tie::IxHash',
'prospect' => '7e0079', #'000000', #black? naw, purple
- 'ordered' => '009999', #teal? cyan?
'active' => '00CC00', #green
+ 'ordered' => '009999', #teal? cyan?
'inactive' => '0000CC', #blue
'suspended' => 'FF9900', #yellow
'cancelled' => 'FF0000', #red
return '';
}
-use Storable qw(thaw);
-use Data::Dumper;
-use MIME::Base64;
sub process_email_search_result {
my $job = shift;
#warn "$me process_re_X $method for job $job\n" if $DEBUG;
$cust_main->bill_and_collect( %args );
}
+sub process_bill_and_collect {
+ my $job = shift;
+ my $param = thaw(decode_base64(shift));
+ my $cust_main = qsearchs( 'cust_main', { custnum => $param->{'custnum'} } )
+ or die "custnum '$param->{custnum}' not found!\n";
+ $param->{'job'} = $job;
+ $param->{'fatal'} = 1; # runs from job queue, will be caught
+ $param->{'retry'} = 1;
+
+ $cust_main->bill_and_collect( %$param );
+}
+
sub _upgrade_data { #class method
my ($class, %opts) = @_;