summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2009-08-12 05:22:08 +0000
committerivan <ivan>2009-08-12 05:22:08 +0000
commit7aad2eb29c444625fd1130f4ed37d89a7da2c027 (patch)
treec01f7f0958c6880d1111c9b6fd5c2aca8cf50920
parentd1014a727cefa5d9813153594541f62ec15fc8b9 (diff)
add pre-bill event stage for late fees, RT#5589
-rw-r--r--FS/FS/cust_main.pm152
-rw-r--r--FS/FS/part_event/Action.pm13
-rw-r--r--FS/FS/part_event/Action/cust_bill_fee_percent.pm2
-rw-r--r--FS/FS/part_event/Action/fee.pm2
4 files changed, 153 insertions, 16 deletions
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index b8657895b..b278e9b14 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2488,7 +2488,6 @@ sub bill {
$options{'not_pkgpart'} ||= {};
- #put below somehow?
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -2502,6 +2501,17 @@ sub bill {
$self->select_for_update; #mutex
+ my $error = $self->do_cust_event(
+ 'debug' => ( $options{'debug'} || 0 ),
+ 'time' => $invoice_time,
+ 'check_freq' => $options{'check_freq'},
+ 'stage' => 'pre-bill',
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
my @cust_bill_pkg = ();
###
@@ -2755,7 +2765,7 @@ sub bill {
'_date' => ( $invoice_time ),
'charged' => $charged,
} );
- my $error = $cust_bill->insert;
+ $error = $cust_bill->insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "can't create invoice for customer #". $self->custnum. ": $error";
@@ -3222,7 +3232,7 @@ sub _gather_taxes {
}
-=item collect OPTIONS
+=item collect [ HASHREF | OPTION => VALUE ... ]
(Attempt to) collect money for this customer's outstanding invoices (see
L<FS::cust_bill>). Usually used after the bill method.
@@ -3247,25 +3257,24 @@ Use this time when deciding when to print invoices and late notices on those inv
Retry card/echeck/LEC transactions even when not scheduled by invoice events.
-=item quiet
-
-set true to surpress email card/ACH decline notices.
-
=item check_freq
"1d" for the traditional, daily events (the default), or "1m" for the new monthly events (part_event.check_freq)
-=item payby
+=item quiet
-allows for one time override of normal customer billing method
+set true to surpress email card/ACH decline notices.
=item debug
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)
-
=back
+# =item payby
+#
+# allows for one time override of normal customer billing method
+
=cut
sub collect {
@@ -3303,12 +3312,107 @@ sub collect {
}
}
+ 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;
+ '';
+
+}
+
+=item do_cust_event [ HASHREF | OPTION => VALUE ... ]
+
+Runs billing events; see L<FS::part_event> and the billing events web
+interface.
+
+If there is an error, returns the error, otherwise returns false.
+
+Options are passed as name-value pairs.
+
+Currently available options are:
+
+=over 4
+
+=item time
+
+Use this time when deciding when to print invoices and late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=item check_freq
+
+"1d" for the traditional, daily events (the default), or "1m" for the new monthly events (part_event.check_freq)
+
+=item stage
+
+"collect" (the default) or "pre-bill"
+
+=item quiet
+
+set true to surpress email card/ACH decline notices.
+
+=item debug
+
+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)
+
+=cut
+
+# =item payby
+#
+# allows for one time override of normal customer billing method
+
+# =item retry
+#
+# Retry card/echeck/LEC transactions even when not scheduled by invoice events.
+
+sub do_cust_event {
+ my( $self, %options ) = @_;
+ my $time = $options{'time'} || time;
+
+ #put below somehow?
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ $self->select_for_update; #mutex
+
+ if ( $DEBUG ) {
+ my $balance = $self->balance;
+ warn "$me do_cust_event customer ". $self->custnum. ": balance $balance\n"
+ }
+
+# if ( exists($options{'retry_card'}) ) {
+# carp 'retry_card option passed to collect is deprecated; use retry';
+# $options{'retry'} ||= $options{'retry_card'};
+# }
+# if ( exists($options{'retry'}) && $options{'retry'} ) {
+# my $error = $self->retry_realtime;
+# if ( $error ) {
+# $dbh->rollback if $oldAutoCommit;
+# return $error;
+# }
+# }
+
# false laziness w/pay_batch::import_results
my $due_cust_event = $self->due_cust_event(
'debug' => ( $options{'debug'} || 0 ),
- 'time' => $invoice_time,
+ 'time' => $time,
'check_freq' => $options{'check_freq'},
+ 'stage' => ( $options{'stage'} || 'collect' ),
);
unless( ref($due_cust_event) ) {
$dbh->rollback if $oldAutoCommit;
@@ -3320,7 +3424,7 @@ sub collect {
#XXX lock event
#re-eval event conditions (a previous event could have changed things)
- unless ( $cust_event->test_conditions( 'time' => $invoice_time ) ) {
+ unless ( $cust_event->test_conditions( 'time' => $time ) ) {
#don't leave stray "new/locked" records around
my $error = $cust_event->delete;
if ( $error ) {
@@ -3373,6 +3477,10 @@ options are:
Search only for events of this check frequency (how often events of this type are checked); currently "1d" (daily, the default) and "1m" (monthly) are recognized.
+=item stage
+
+"collect" (the default) or "pre-bill"
+
=item time
"Current time" for the events.
@@ -3428,7 +3536,7 @@ sub due_cust_event {
unless $opt{testonly};
###
- # 1: find possible events (initial search)
+ # find possible events (initial search)
###
my @cust_event = ();
@@ -3519,8 +3627,20 @@ sub due_cust_event {
" total possible cust events found in initial search\n"
if $DEBUG; # > 1;
+
+ ##
+ # test stage
+ ##
+
+ $opt{stage} ||= 'collect';
+ @cust_event =
+ grep { my $stage = $_->part_event->event_stage;
+ $opt{stage} eq $stage or ( ! $stage && $opt{stage} eq 'collect' )
+ }
+ @cust_event;
+
##
- # 2: test conditions
+ # test conditions
##
my %unsat = ();
@@ -3537,7 +3657,7 @@ sub due_cust_event {
if $DEBUG; # > 1;
##
- # 3: insert
+ # insert
##
unless( $opt{testonly} ) {
@@ -3555,7 +3675,7 @@ sub due_cust_event {
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
##
- # 4: return
+ # return
##
warn " returning events: ". Dumper(@cust_event). "\n"
diff --git a/FS/FS/part_event/Action.pm b/FS/FS/part_event/Action.pm
index 57239d78e..45219a321 100644
--- a/FS/FS/part_event/Action.pm
+++ b/FS/FS/part_event/Action.pm
@@ -54,6 +54,19 @@ sub eventtable_hashref {
};
}
+=item event_stage
+
+Action classes may define an event_stage method to indicate a preference
+for being run at a non-standard stage of the billing and collection process.
+
+This method may currently return "collect" (the default) or "pre-bill".
+
+=cut
+
+sub event_stage {
+ 'collect';
+}
+
=item option_fields
Action classes may define an option_fields method to indicate that they
diff --git a/FS/FS/part_event/Action/cust_bill_fee_percent.pm b/FS/FS/part_event/Action/cust_bill_fee_percent.pm
index 354778f7d..9206c6a33 100644
--- a/FS/FS/part_event/Action/cust_bill_fee_percent.pm
+++ b/FS/FS/part_event/Action/cust_bill_fee_percent.pm
@@ -9,6 +9,8 @@ sub eventtable_hashref {
{ 'cust_bill' => 1 };
}
+sub event_stage { 'pre-bill'; }
+
sub option_fields {
(
'percent' => { label=>'Percent', size=>2, },
diff --git a/FS/FS/part_event/Action/fee.pm b/FS/FS/part_event/Action/fee.pm
index 6ea7103ab..6450225a2 100644
--- a/FS/FS/part_event/Action/fee.pm
+++ b/FS/FS/part_event/Action/fee.pm
@@ -5,6 +5,8 @@ use base qw( FS::part_event::Action );
sub description { 'Late fee (flat)'; }
+sub event_stage { 'pre-bill'; }
+
sub option_fields {
(
'charge' => { label=>'Amount', type=>'money', }, # size=>7, },