projects
/
freeside.git
/ commitdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
| commitdiff |
tree
raw
|
patch
|
inline
| side by side (parent:
68cdfc7
)
RT# 78547 Allow for simulated billing within a transaction
author
Mitch Jackson
<mitch@freeside.biz>
Tue, 11 Sep 2018 07:23:52 +0000
(
03:23
-0400)
committer
Mitch Jackson
<mitch@freeside.biz>
Tue, 11 Sep 2018 07:23:52 +0000
(
03:23
-0400)
FS/FS/Misc/Savepoint.pm
patch
|
blob
|
history
FS/FS/UID.pm
patch
|
blob
|
history
FS/FS/cust_bill.pm
patch
|
blob
|
history
FS/FS/cust_main.pm
patch
|
blob
|
history
FS/FS/cust_main/Billing.pm
patch
|
blob
|
history
FS/FS/cust_main/Billing_Realtime.pm
patch
|
blob
|
history
FS/FS/part_export/nena2.pm
patch
|
blob
|
history
diff --git
a/FS/FS/Misc/Savepoint.pm
b/FS/FS/Misc/Savepoint.pm
index
b15b36d
..
f8e2c5f
100644
(file)
--- a/
FS/FS/Misc/Savepoint.pm
+++ b/
FS/FS/Misc/Savepoint.pm
@@
-55,7
+55,7
@@
Savepoints cannot work while AutoCommit is enabled.
Savepoint labels must be valid sql identifiers. If your choice of label
would not make a valid column name, it probably will not make a valid label.
Savepoint labels must be valid sql identifiers. If your choice of label
would not make a valid column name, it probably will not make a valid label.
-Savepint labels must be unique within the transaction.
+Savep
o
int labels must be unique within the transaction.
=cut
=cut
diff --git
a/FS/FS/UID.pm
b/FS/FS/UID.pm
index
50a9178
..
693e5d9
100644
(file)
--- a/
FS/FS/UID.pm
+++ b/
FS/FS/UID.pm
@@
-5,7
+5,7
@@
use strict;
use vars qw(
@EXPORT_OK $DEBUG $me $cgi $freeside_uid $conf_dir $cache_dir
$secrets $datasrc $db_user $db_pass $schema $dbh $driver_name
use vars qw(
@EXPORT_OK $DEBUG $me $cgi $freeside_uid $conf_dir $cache_dir
$secrets $datasrc $db_user $db_pass $schema $dbh $driver_name
- $AutoCommit %callback @callback $callback_hack
+ $AutoCommit
$ForceObeyAutoCommit
%callback @callback $callback_hack
);
use subs qw( getsecrets );
use Carp qw( carp croak cluck confess );
);
use subs qw( getsecrets );
use Carp qw( carp croak cluck confess );
@@
-26,7
+26,17
@@
$freeside_uid = scalar(getpwnam('freeside'));
$conf_dir = "%%%FREESIDE_CONF%%%";
$cache_dir = "%%%FREESIDE_CACHE%%%";
$conf_dir = "%%%FREESIDE_CONF%%%";
$cache_dir = "%%%FREESIDE_CACHE%%%";
+# Code wanting to issue a COMMIT statement to the database is expected to
+# obey the convention of checking this flag first. Setting $AutoCommit = 0
+# should (usually) suppress COMMIT statements.
$AutoCommit = 1; #ours, not DBI
$AutoCommit = 1; #ours, not DBI
+
+# Not all methods obey $AutoCommit, by design choice. Setting
+# $ForceObeyAutoCommit = 1 will override that design choice for:
+# &FS::cust_main::Billing::collect
+# &FS::cust_main::Billing::do_cust_event
+$ForceObeyAutoCommit = 0;
+
$callback_hack = 0;
=head1 NAME
$callback_hack = 0;
=head1 NAME
diff --git
a/FS/FS/cust_bill.pm
b/FS/FS/cust_bill.pm
index
47f71c4
..
7158cb2
100644
(file)
--- a/
FS/FS/cust_bill.pm
+++ b/
FS/FS/cust_bill.pm
@@
-41,6
+41,7
@@
use FS::cust_bill_void;
use FS::reason;
use FS::reason_type;
use FS::L10N;
use FS::reason;
use FS::reason_type;
use FS::L10N;
+use FS::Misc::Savepoint;
$DEBUG = 0;
$me = '[FS::cust_bill]';
$DEBUG = 0;
$me = '[FS::cust_bill]';
@@
-974,6
+975,9
@@
sub apply_payments_and_credits {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ my $savepoint_label = 'cust_bill__apply_payments_and_credits';
+ savepoint_create( $savepoint_label );
+
$self->select_for_update; #mutex
my @payments = grep { $_->unapplied > 0 }
$self->select_for_update; #mutex
my @payments = grep { $_->unapplied > 0 }
@@
-1062,6
+1066,7
@@
sub apply_payments_and_credits {
my $error = $app->insert(%options);
if ( $error ) {
my $error = $app->insert(%options);
if ( $error ) {
+ savepoint_rollback_and_release( $savepoint_label );
$dbh->rollback if $oldAutoCommit;
return "Error inserting ". $app->table. " record: $error";
}
$dbh->rollback if $oldAutoCommit;
return "Error inserting ". $app->table. " record: $error";
}
@@
-1069,6
+1074,7
@@
sub apply_payments_and_credits {
}
}
+ savepoint_release( $savepoint_label );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
diff --git
a/FS/FS/cust_main.pm
b/FS/FS/cust_main.pm
index
ea524da
..
2e8fe81
100644
(file)
--- a/
FS/FS/cust_main.pm
+++ b/
FS/FS/cust_main.pm
@@
-79,6
+79,7
@@
use FS::sales;
use FS::cust_payby;
use FS::contact;
use FS::reason;
use FS::cust_payby;
use FS::contact;
use FS::reason;
+use FS::Misc::Savepoint;
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
@@
-2212,11
+2213,15
@@
sub cancel_pkgs {
my( $self, %opt ) = @_;
# we're going to cancel services, which is not reversible
my( $self, %opt ) = @_;
# we're going to cancel services, which is not reversible
+ # unless exports are suppressed
die "cancel_pkgs cannot be run inside a transaction"
die "cancel_pkgs cannot be run inside a transaction"
- if
$FS::UID::AutoCommit == 0
;
+ if
!$FS::UID::AutoCommit && !$FS::svc_Common::noexport_hack
;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
local $FS::UID::AutoCommit = 0;
+ savepoint_create('cancel_pkgs');
+
return ( 'access denied' )
unless $FS::CurrentUser::CurrentUser->access_right('Cancel customer');
return ( 'access denied' )
unless $FS::CurrentUser::CurrentUser->access_right('Cancel customer');
@@
-2233,7
+2238,8
@@
sub cancel_pkgs {
my $ban = new FS::banned_pay $cust_payby->_new_banned_pay_hashref;
my $error = $ban->insert;
if ($error) {
my $ban = new FS::banned_pay $cust_payby->_new_banned_pay_hashref;
my $error = $ban->insert;
if ($error) {
- dbh->rollback;
+ savepoint_rollback_and_release('cancel_pkgs');
+ dbh->rollback if $oldAutoCommit;
return ( $error );
}
return ( $error );
}
@@
-2253,11
+2259,13
@@
sub cancel_pkgs {
'time' => $cancel_time );
if ($error) {
warn "Error billing during cancel, custnum ". $self->custnum. ": $error";
'time' => $cancel_time );
if ($error) {
warn "Error billing during cancel, custnum ". $self->custnum. ": $error";
- dbh->rollback;
+ savepoint_rollback_and_release('cancel_pkgs');
+ dbh->rollback if $oldAutoCommit;
return ( "Error billing during cancellation: $error" );
}
}
return ( "Error billing during cancellation: $error" );
}
}
- dbh->commit;
+ savepoint_release('cancel_pkgs');
+ dbh->commit if $oldAutoCommit;
my @errors;
# try to cancel each service, the same way we would for individual packages,
my @errors;
# try to cancel each service, the same way we would for individual packages,
@@
-2271,17
+2279,22
@@
sub cancel_pkgs {
warn "$me removing ".scalar(@sorted_cust_svc)." service(s) for customer ".
$self->custnum."\n"
if $DEBUG;
warn "$me removing ".scalar(@sorted_cust_svc)." service(s) for customer ".
$self->custnum."\n"
if $DEBUG;
+ my $i = 0;
foreach my $cust_svc (@sorted_cust_svc) {
foreach my $cust_svc (@sorted_cust_svc) {
+ my $savepoint = 'cancel_pkgs_'.$i++;
+ savepoint_create( $savepoint );
my $part_svc = $cust_svc->part_svc;
next if ( defined($part_svc) and $part_svc->preserve );
# immediate cancel, no date option
# transactionize individually
my $error = try { $cust_svc->cancel } catch { $_ };
if ( $error ) {
my $part_svc = $cust_svc->part_svc;
next if ( defined($part_svc) and $part_svc->preserve );
# immediate cancel, no date option
# transactionize individually
my $error = try { $cust_svc->cancel } catch { $_ };
if ( $error ) {
- dbh->rollback;
+ savepoint_rollback_and_release( $savepoint );
+ dbh->rollback if $oldAutoCommit;
push @errors, $error;
} else {
push @errors, $error;
} else {
- dbh->commit;
+ savepoint_release( $savepoint );
+ dbh->commit if $oldAutoCommit;
}
}
if (@errors) {
}
}
if (@errors) {
@@
-2297,8
+2310,11
@@
sub cancel_pkgs {
@cprs = @{ delete $opt{'cust_pkg_reason'} };
}
my $null_reason;
@cprs = @{ delete $opt{'cust_pkg_reason'} };
}
my $null_reason;
+ $i = 0;
foreach (@pkgs) {
my %lopt = %opt;
foreach (@pkgs) {
my %lopt = %opt;
+ my $savepoint = 'cancel_pkgs_'.$i++;
+ savepoint_create( $savepoint );
if (@cprs) {
my $cpr = shift @cprs;
if ( $cpr ) {
if (@cprs) {
my $cpr = shift @cprs;
if ( $cpr ) {
@@
-2319,10
+2335,12
@@
sub cancel_pkgs {
}
my $error = $_->cancel(%lopt);
if ( $error ) {
}
my $error = $_->cancel(%lopt);
if ( $error ) {
- dbh->rollback;
+ savepoint_rollback_and_release( $savepoint );
+ dbh->rollback if $oldAutoCommit;
push @errors, 'pkgnum '.$_->pkgnum.': '.$error;
} else {
push @errors, 'pkgnum '.$_->pkgnum.': '.$error;
} else {
- dbh->commit;
+ savepoint_release( $savepoint );
+ dbh->commit if $oldAutoCommit;
}
}
}
}
diff --git
a/FS/FS/cust_main/Billing.pm
b/FS/FS/cust_main/Billing.pm
index
71d5c9b
..
1be7d39
100644
(file)
--- a/
FS/FS/cust_main/Billing.pm
+++ b/
FS/FS/cust_main/Billing.pm
@@
-26,6
+26,7
@@
use FS::pkg_category;
use FS::FeeOrigin_Mixin;
use FS::Log;
use FS::TaxEngine;
use FS::FeeOrigin_Mixin;
use FS::Log;
use FS::TaxEngine;
+use FS::Misc::Savepoint;
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
@@
-1753,7
+1754,10
@@
sub collect {
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
#never want to roll back an event just because it returned an error
$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;
+ # unless $FS::UID::ForceObeyAutoCommit is set
+ local $FS::UID::AutoCommit = 1
+ unless !$oldAutoCommit
+ && $FS::UID::ForceObeyAutoCommit;
$self->do_cust_event(
'debug' => ( $options{'debug'} || 0 ),
$self->do_cust_event(
'debug' => ( $options{'debug'} || 0 ),
@@
-1961,9
+1965,13
@@
sub do_cust_event {
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
}
$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
#never want to roll back an event just because it or a different one
# returned an error
- local $FS::UID::AutoCommit = 1; #$oldAutoCommit;
+ # unless $FS::UID::ForceObeyAutoCommit is set
+ local $FS::UID::AutoCommit = 1
+ unless !$oldAutoCommit
+ && $FS::UID::ForceObeyAutoCommit;
foreach my $cust_event ( @$due_cust_event ) {
foreach my $cust_event ( @$due_cust_event ) {
@@
-2288,16
+2296,21
@@
sub apply_payments_and_credits {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ my $savepoint_label = 'Billing__apply_payments_and_credits';
+ savepoint_create( $savepoint_label );
+
$self->select_for_update; #mutex
foreach my $cust_bill ( $self->open_cust_bill ) {
my $error = $cust_bill->apply_payments_and_credits(%options);
if ( $error ) {
$self->select_for_update; #mutex
foreach my $cust_bill ( $self->open_cust_bill ) {
my $error = $cust_bill->apply_payments_and_credits(%options);
if ( $error ) {
+ savepoint_rollback_and_release( $savepoint_label );
$dbh->rollback if $oldAutoCommit;
return "Error applying: $error";
}
}
$dbh->rollback if $oldAutoCommit;
return "Error applying: $error";
}
}
+ savepoint_release( $savepoint_label );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
diff --git
a/FS/FS/cust_main/Billing_Realtime.pm
b/FS/FS/cust_main/Billing_Realtime.pm
index
d286f63
..
714a2e6
100644
(file)
--- a/
FS/FS/cust_main/Billing_Realtime.pm
+++ b/
FS/FS/cust_main/Billing_Realtime.pm
@@
-16,6
+16,7
@@
use FS::cust_bill_pay;
use FS::cust_refund;
use FS::banned_pay;
use FS::payment_gateway;
use FS::cust_refund;
use FS::banned_pay;
use FS::payment_gateway;
+use FS::Misc::Savepoint;
$realtime_bop_decline_quiet = 0;
$realtime_bop_decline_quiet = 0;
@@
-27,6
+28,7
@@
$me = '[FS::cust_main::Billing_Realtime]';
our $BOP_TESTING = 0;
our $BOP_TESTING_SUCCESS = 1;
our $BOP_TESTING = 0;
our $BOP_TESTING_SUCCESS = 1;
+our $BOP_TESTING_TIMESTAMP = '';
install_callback FS::UID sub {
$conf = new FS::Conf;
install_callback FS::UID sub {
$conf = new FS::Conf;
@@
-405,7
+407,7
@@
sub realtime_bop {
confess "Can't call realtime_bop within another transaction ".
'($FS::UID::AutoCommit is false)'
confess "Can't call realtime_bop within another transaction ".
'($FS::UID::AutoCommit is false)'
- unless $FS::UID::AutoCommit;
+ unless $FS::UID::AutoCommit
|| $BOP_TESTING
;
local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
@@
-682,7
+684,7
@@
sub realtime_bop {
my $cust_pay_pending = new FS::cust_pay_pending {
'custnum' => $self->custnum,
'paid' => $options{amount},
my $cust_pay_pending = new FS::cust_pay_pending {
'custnum' => $self->custnum,
'paid' => $options{amount},
- '_date' => '',
+ '_date' =>
$BOP_TESTING ? $BOP_TESTING_TIMESTAMP :
'',
'payby' => $bop_method2payby{$options{method}},
'payinfo' => $options{payinfo},
'paymask' => $options{paymask},
'payby' => $bop_method2payby{$options{method}},
'payinfo' => $options{payinfo},
'paymask' => $options{paymask},
@@
-757,7
+759,7
@@
sub realtime_bop {
return { reference => $cust_pay_pending->paypendingnum,
map { $_ => $transaction->$_ } qw ( popup_url collectitems ) };
return { reference => $cust_pay_pending->paypendingnum,
map { $_ => $transaction->$_ } qw ( popup_url collectitems ) };
- } elsif ( $transaction->is_success() && $action2 ) {
+ } elsif (
!$BOP_TESTING &&
$transaction->is_success() && $action2 ) {
$cust_pay_pending->status('authorized');
my $cpp_authorized_err = $cust_pay_pending->replace;
$cust_pay_pending->status('authorized');
my $cpp_authorized_err = $cust_pay_pending->replace;
@@
-946,7
+948,7
@@
sub _realtime_bop_result {
'custnum' => $self->custnum,
'invnum' => $options{'invnum'},
'paid' => $cust_pay_pending->paid,
'custnum' => $self->custnum,
'invnum' => $options{'invnum'},
'paid' => $cust_pay_pending->paid,
- '_date' => '',
+ '_date' =>
$BOP_TESTING ? $BOP_TESTING_TIMESTAMP :
'',
'payby' => $cust_pay_pending->payby,
'payinfo' => $options{'payinfo'},
'paymask' => $options{'paymask'} || $cust_pay_pending->paymask,
'payby' => $cust_pay_pending->payby,
'payinfo' => $options{'payinfo'},
'paymask' => $options{'paymask'} || $cust_pay_pending->paymask,
@@
-967,12
+969,16
@@
sub _realtime_bop_result {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ my $savepoint_label = '_realtime_bop_result';
+ savepoint_create( $savepoint_label );
+
#start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction
my $error = $cust_pay->insert($options{'manual'} ? ( 'manual' => 1 ) : () );
if ( $error ) {
#start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction
my $error = $cust_pay->insert($options{'manual'} ? ( 'manual' => 1 ) : () );
if ( $error ) {
- $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ savepoint_rollback( $savepoint_label );
+
$cust_pay->invnum(''); #try again with no specific invnum
$cust_pay->paynum('');
my $error2 = $cust_pay->insert( $options{'manual'} ?
$cust_pay->invnum(''); #try again with no specific invnum
$cust_pay->paynum('');
my $error2 = $cust_pay->insert( $options{'manual'} ?
@@
-981,7
+987,8
@@
sub _realtime_bop_result {
if ( $error2 ) {
# gah. but at least we have a record of the state we had to abort in
# from cust_pay_pending now.
if ( $error2 ) {
# gah. but at least we have a record of the state we had to abort in
# from cust_pay_pending now.
- $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ savepoint_rollback_and_release( $savepoint_label );
+
my $e = "WARNING: $options{method} captured but payment not recorded -".
" error inserting payment (". $payment_gateway->gateway_module.
"): $error2".
my $e = "WARNING: $options{method} captured but payment not recorded -".
" error inserting payment (". $payment_gateway->gateway_module.
"): $error2".
@@
-996,9
+1003,10
@@
sub _realtime_bop_result {
my $jobnum = $cust_pay_pending->jobnum;
if ( $jobnum ) {
my $placeholder = qsearchs( 'queue', { 'jobnum' => $jobnum } );
my $jobnum = $cust_pay_pending->jobnum;
if ( $jobnum ) {
my $placeholder = qsearchs( 'queue', { 'jobnum' => $jobnum } );
-
+
unless ( $placeholder ) {
unless ( $placeholder ) {
- $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ savepoint_rollback_and_release( $savepoint_label );
+
my $e = "WARNING: $options{method} captured but job $jobnum not ".
"found for paypendingnum ". $cust_pay_pending->paypendingnum. "\n";
warn $e;
my $e = "WARNING: $options{method} captured but job $jobnum not ".
"found for paypendingnum ". $cust_pay_pending->paypendingnum. "\n";
warn $e;
@@
-1008,7
+1016,8
@@
sub _realtime_bop_result {
$error = $placeholder->delete;
if ( $error ) {
$error = $placeholder->delete;
if ( $error ) {
- $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
+ savepoint_rollback_and_release( $savepoint_label );
+
my $e = "WARNING: $options{method} captured but could not delete ".
"job $jobnum for paypendingnum ".
$cust_pay_pending->paypendingnum. ": $error\n";
my $e = "WARNING: $options{method} captured but could not delete ".
"job $jobnum for paypendingnum ".
$cust_pay_pending->paypendingnum. ": $error\n";
@@
-1030,8
+1039,8
@@
sub _realtime_bop_result {
my $cpp_done_err = $cust_pay_pending->replace;
if ( $cpp_done_err ) {
my $cpp_done_err = $cust_pay_pending->replace;
if ( $cpp_done_err ) {
+ savepoint_rollback_and_release( $savepoint_label );
- $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
my $e = "WARNING: $options{method} captured but payment not recorded - ".
"error updating status for paypendingnum ".
$cust_pay_pending->paypendingnum. ": $cpp_done_err \n";
my $e = "WARNING: $options{method} captured but payment not recorded - ".
"error updating status for paypendingnum ".
$cust_pay_pending->paypendingnum. ": $cpp_done_err \n";
@@
-1039,7
+1048,7
@@
sub _realtime_bop_result {
return $e;
} else {
return $e;
} else {
-
+ savepoint_release( $savepoint_label );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
if ( $options{'apply'} ) {
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
if ( $options{'apply'} ) {
diff --git
a/FS/FS/part_export/nena2.pm
b/FS/FS/part_export/nena2.pm
index
f6a730e
..
cc4069c
100644
(file)
--- a/
FS/FS/part_export/nena2.pm
+++ b/
FS/FS/part_export/nena2.pm
@@
-10,6
+10,7
@@
use Date::Format qw(time2str);
use Parse::FixedLength;
use File::Temp qw(tempfile);
use vars qw(%info %options $initial_load_hack $DEBUG);
use Parse::FixedLength;
use File::Temp qw(tempfile);
use vars qw(%info %options $initial_load_hack $DEBUG);
+use Carp qw( carp );
my %upload_targets;
my %upload_targets;
@@
-396,6
+397,13
@@
sub process {
my $self = shift;
my $batch = shift;
local $DEBUG = $self->option('debug');
my $self = shift;
my $batch = shift;
local $DEBUG = $self->option('debug');
+
+ if ( $FS::svc_Common::noexport_hack ) {
+ carp 'FS::part_export::nena2::process() suppressed by noexport_hack'
+ if $DEBUG;
+ return;
+ }
+
local $FS::UID::AutoCommit = 0;
my $error;
local $FS::UID::AutoCommit = 0;
my $error;