package FS::cust_event;
+use base qw( FS::cust_main_Mixin FS::Record );
use strict;
-use vars qw( @ISA $DEBUG $me );
+use vars qw( $DEBUG $me );
use Carp qw( croak confess );
use FS::Record qw( qsearch qsearchs dbdef );
-use FS::cust_main_Mixin;
-use FS::part_event;
#for cust_X
use FS::cust_main;
use FS::cust_pkg;
use FS::cust_bill;
-
-@ISA = qw(FS::cust_main_Mixin FS::Record);
+use FS::cust_pay;
+use FS::svc_acct;
$DEBUG = 0;
$me = '[FS::cust_event]';
=item statustext - additional status detail (i.e. error or progress message)
+=item no_action - 'Y' if the event action wasn't performed. Some actions
+contain an internal check to see if the action is going to be impossible (for
+example, emailing a notice to a customer who has no email address), and if so,
+won't attempt the action. It shouldn't be reported as a failure because
+there's no need to retry it. However, the action should set no_action = 'Y'
+so that there's a record.
+
=back
=head1 METHODS
sub table { 'cust_event'; }
-sub cust_linked { $_[0]->cust_main_custnum; }
+sub cust_linked { $_[0]->cust_main_custnum || $_[0]->custnum }
sub cust_unlinked_msg {
my $self = shift;
"WARNING: can't find cust_main.custnum ". $self->custnum;
$dbdef_eventtable->primary_key
)
|| $self->ut_number('_date')
- || $self->ut_enum('status', [qw( new locked done failed )])
+ || $self->ut_enum('status', [qw( new locked done failed initial)])
|| $self->ut_anything('statustext')
+ || $self->ut_flag('no_action')
;
return $error if $error;
Returns the event definition (see L<FS::part_event>) for this completed event.
-=cut
-
-sub part_event {
- my $self = shift;
- qsearchs( 'part_event', { 'eventpart' => $self->eventpart } );
-}
-
=item cust_X
Returns the customer, package, invoice or batched payment (see
my $object = $self->cust_X;
my @conditions = $part_event->part_event_condition;
$opt{'cust_event'} = $self;
+ $opt{'time'} = $self->_date
+ or die "test_conditions called without cust_event._date\n";
+ # this MUST be set, or all hell breaks loose in event conditions.
+ # it MUST be in the same time as in the cust_event object, or
+ # future time-dependent events will trigger incorrectly.
#no unsatisfied conditions
#! grep ! $_->condition( $object, %opt ), @conditions;
sub do_event {
my $self = shift;
+ my %opt = @_; # currently only 'time'
+ my $time = $opt{'time'} || time;
my $part_event = $self->part_event;
" (". $part_event->action. ") $for\n"
if $DEBUG;
- my $oldAutoCommit = $FS::UID::AutoCommit;
- local $FS::UID::AutoCommit = 0;
-
my $error;
{
local $SIG{__DIE__}; # don't want Mason __DIE__ handler active
- $error = eval { $part_event->do_action($object); };
+ $error = eval { $part_event->do_action($object, $self); };
}
my $status = '';
$statustext = "Error running ". $part_event->action. " action: $@";
} elsif ( $error ) {
$status = 'done';
- $statustext = $error;
+ if ( $error eq 'N/A' ) {
+ # archaic way to indicate no-op completion of spool_csv (and maybe
+ # other events)?
+ $self->no_action('Y');
+ } else {
+ $statustext = $error;
+ }
} else {
$status = 'done';
}
#replace or add myself
- $self->_date(time);
+ $self->_date($time);
$self->status($status);
$self->statustext($statustext);
$self->replace($old);
}
-=item join_cust_sql
+=item join_sql
=cut
"
JOIN part_event USING ( eventpart )
- LEFT JOIN cust_bill ON ( eventtable = 'cust_bill' AND tablenum = invnum )
- LEFT JOIN cust_pkg ON ( eventtable = 'cust_pkg' AND tablenum = pkgnum )
- LEFT JOIN cust_main ON ( ( eventtable = 'cust_main' AND tablenum = cust_main.custnum )
- OR ( eventtable = 'cust_bill' AND cust_bill.custnum = cust_main.custnum )
- OR ( eventtable = 'cust_pkg' AND cust_pkg.custnum = cust_main.custnum )
- )
+
+ LEFT JOIN cust_bill ON ( eventtable = 'cust_bill' AND tablenum = cust_bill.invnum )
+ LEFT JOIN cust_pkg ON ( eventtable = 'cust_pkg' AND tablenum = cust_pkg.pkgnum )
+ LEFT JOIN cust_pay ON ( eventtable = 'cust_pay' AND tablenum = cust_pay.paynum )
+ LEFT JOIN cust_pay_batch ON ( eventtable = 'cust_pay_batch' AND tablenum = cust_pay_batch.paybatchnum )
+ LEFT JOIN cust_statement ON ( eventtable = 'cust_statement' AND tablenum = cust_statement.statementnum )
+
+ LEFT JOIN cust_svc ON ( eventtable = 'svc_acct' AND tablenum = cust_svc.svcnum )
+ LEFT JOIN cust_pkg AS cust_pkg_for_svc ON ( cust_svc.pkgnum = cust_pkg_for_svc.pkgnum )
+
+ LEFT JOIN cust_main ON (
+ ( eventtable = 'cust_main' AND tablenum = cust_main.custnum )
+ OR ( eventtable = 'cust_bill' AND cust_bill.custnum = cust_main.custnum )
+ OR ( eventtable = 'cust_pkg' AND cust_pkg.custnum = cust_main.custnum )
+ OR ( eventtable = 'cust_pay' AND cust_pay.custnum = cust_main.custnum )
+ OR ( eventtable = 'svc_acct' AND cust_pkg_for_svc.custnum = cust_main.custnum )
+ )
";
}
=item pkgnum
+=item svcnum
+
=item failed
=item beginning
=item ending
-=item payby
-
-=item
-
=back
=cut
push @search, "cust_event._date <= $1";
}
- if ( $param->{'failed'} ) {
- push @search, "statustext != ''",
- "statustext IS NOT NULL",
- "statustext != 'N/A'";
- }
+ #if ( $param->{'failed'} ) {
+ # push @search, "statustext != ''",
+ # "statustext IS NOT NULL",
+ # "statustext != 'N/A'";
+ #}
+ # huh?
+
+ my @event_status = ref($param->{'event_status'})
+ ? @{ $param->{'event_status'} }
+ : split(',', $param->{'event_status'});
+ if ( @event_status ) {
+ my @status;
+
+ my ($done_Y, $done_N, $done_S);
+ # done_Y: action was taken
+ # done_N: action was not taken
+ # done_S: status message returned
+ foreach (@event_status) {
+ if ($_ eq 'done_Y') {
+ $done_Y = 1;
+ } elsif ( $_ eq 'done_N' ) {
+ $done_N = 1;
+ } elsif ( $_ eq 'done_S' ) {
+ $done_S = 1;
+ } else {
+ push @status, $_;
+ }
+ }
+ if ( $done_Y or $done_N or $done_S ) {
+ push @status, 'done';
+ }
+ if ( @status ) {
+ push @search, "cust_event.status IN(" .
+ join(',', map "'$_'", @status) .
+ ')';
+ }
+
+ # done_S status should include only those where statustext is not null,
+ # and done_Y should include only those where it is.
+ if ( $done_Y and $done_N and $done_S ) {
+ # then not necessary
+ } else {
+ my @done_status;
+ if ( $done_Y ) {
+ push @done_status, "(cust_event.no_action IS NULL AND cust_event.statustext IS NULL)";
+ }
+ if ( $done_N ) {
+ push @done_status, "(cust_event.no_action = 'Y')";
+ }
+ if ( $done_S ) {
+ push @done_status, "(cust_event.no_action IS NULL AND cust_event.statustext IS NOT NULL)";
+ }
+ push @search, join(' OR ', @done_status) if @done_status;
+ }
+
+ } # event_status
+
+ # always hide initialization
+ push @search, 'cust_event.status != \'initial\'';
if ( $param->{'custnum'} =~ /^(\d+)$/ ) {
push @search, "cust_main.custnum = '$1'";
"tablenum = '$1'";
}
+ if ( $param->{'paynum'} =~ /^(\d+)$/ ) {
+ push @search, "part_event.eventtable = 'cust_pay'",
+ "tablenum = '$1'";
+ }
+
+ if ( $param->{'svcnum'} =~ /^(\d+)$/ ) {
+ push @search, "part_event.eventtable = 'svc_acct'",
+ "tablenum = '$1'";
+ }
+
my $where = 'WHERE '. join(' AND ', @search );
join(' AND ', @search );
process_re_X('fax', @_);
}
-use Storable qw(thaw);
use Data::Dumper;
-use MIME::Base64;
sub process_re_X {
- my( $method, $job ) = ( shift, shift );
-
- my $param = thaw(decode_base64(shift));
+ my( $method, $job, $param ) = @_;
warn Dumper($param) if $DEBUG;
re_X(
my $cust_X = $cust_event->cust_X; # cust_bill
next unless $cust_X->can($method);
- $cust_X->$method( $cust_event->part_event->templatename
- || $cust_X->agent_template
- );
+ my $part_event = $cust_event->part_event;
+ my $template = $part_event->templatename
+ || $cust_X->agent_template;
+ my $modenum = $part_event->option('modenum') || '';
+ my $invoice_from = $part_event->option('agent_invoice_from') || '';
+ $cust_X->set('mode' => $modenum);
+ $cust_X->$method( { template => $template,
+ modenum => $modenum,
+ from => $invoice_from,
+ } );
if ( $job ) { #progressbar foo
$num++;