summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2018-09-13 13:02:28 -0700
committerIvan Kohler <ivan@freeside.biz>2018-09-13 13:02:28 -0700
commitdfeca08f8d935d127d99de4690e2d5edf4f78b95 (patch)
tree610fb469c9ccdfffb923f1a002659c96e897bfab /FS
parent833cfe5c9938d33c3e6b97ed610c25a7afa6eb04 (diff)
parent0682747829a56d487155e28675c133cb90f991de (diff)
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm30
-rw-r--r--FS/FS/ClientAPI_XMLRPC.pm1
-rw-r--r--FS/FS/Misc/Savepoint.pm2
-rw-r--r--FS/FS/UID.pm12
-rw-r--r--FS/FS/access_user.pm98
-rw-r--r--FS/FS/contact.pm13
-rw-r--r--FS/FS/cust_bill.pm6
-rw-r--r--FS/FS/cust_main.pm34
-rw-r--r--FS/FS/cust_main/Billing.pm17
-rw-r--r--FS/FS/cust_main/Billing_Realtime.pm31
-rw-r--r--FS/FS/cust_payby.pm74
-rw-r--r--FS/FS/part_export/nena2.pm8
-rw-r--r--FS/FS/part_export/saisei.pm60
13 files changed, 355 insertions, 31 deletions
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
index 263b311..57d4298 100644
--- a/FS/FS/ClientAPI/MyAccount.pm
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -184,6 +184,29 @@ sub skin_info {
}
+sub get_mac_address {
+ my $p = shift;
+
+## access radius exports acct tables to get mac
+ my @part_export = ();
+ @part_export = (
+ qsearch( 'part_export', { 'exporttype' => 'sqlradius' } ),
+ qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } ),
+ qsearch( 'part_export', { 'exporttype' => 'broadband_sqlradius' } ),
+ );
+
+ my @sessions;
+ foreach my $part_export (@part_export) {
+ push @sessions, ( @{ $part_export->usage_sessions( {
+ 'ip' => $p->{'ip'},
+ } ) } );
+ }
+
+ my $mac = $sessions[0]->{'callingstationid'};
+
+ return { 'mac_address' => $mac, };
+}
+
sub login_info {
my $p = shift;
@@ -239,8 +262,11 @@ sub login {
} elsif ( $p->{'domain'} eq 'ip_mac' ) {
- my $svc_broadband = qsearchs( 'svc_broadband', { 'mac_addr' => $p->{'username'} } );
- return { error => 'IP address not found' }
+ my $mac_address = $p->{'username'};
+ $mac_address =~ s/\://g;
+
+ my $svc_broadband = qsearchs( 'svc_broadband', { 'mac_addr' => $mac_address } );
+ return { error => 'MAC address not found '.$p->{'username'} }
unless $svc_broadband;
$svc_x = $svc_broadband;
diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm
index dcf34fd..db0537c 100644
--- a/FS/FS/ClientAPI_XMLRPC.pm
+++ b/FS/FS/ClientAPI_XMLRPC.pm
@@ -227,6 +227,7 @@ sub ss2clientapi {
'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
'quotation_order' => 'MyAccount/quotation/quotation_order',
+ 'get_mac_address' => 'MyAccount/get_mac_address',
'freesideinc_service' => 'Freeside/freesideinc_service',
};
diff --git a/FS/FS/Misc/Savepoint.pm b/FS/FS/Misc/Savepoint.pm
index b15b36d..f8e2c5f 100644
--- 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.
-Savepint labels must be unique within the transaction.
+Savepoint labels must be unique within the transaction.
=cut
diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm
index 50a9178..693e5d9 100644
--- 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
- $AutoCommit %callback @callback $callback_hack
+ $AutoCommit $ForceObeyAutoCommit %callback @callback $callback_hack
);
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%%%";
+# 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
+
+# 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
diff --git a/FS/FS/access_user.pm b/FS/FS/access_user.pm
index a9fdf5b..f23aa77 100644
--- a/FS/FS/access_user.pm
+++ b/FS/FS/access_user.pm
@@ -12,6 +12,7 @@ use FS::Record qw( qsearch qsearchs dbh );
use FS::agent;
use FS::cust_main;
use FS::sales;
+use Carp qw( croak );
$DEBUG = 0;
$me = '[FS::access_user]';
@@ -814,6 +815,103 @@ sub set_page_pref {
return $error;
}
+=item get_pref NAME
+
+Fetch the prefvalue column from L<FS::access_user_pref> for prefname NAME
+
+Returns undef when no value has been saved, or when record has expired
+
+=cut
+
+sub get_pref {
+ my ( $self, $prefname ) = @_;
+ croak 'prefname parameter requrired' unless $prefname;
+
+ my $pref_row = $self->get_pref_row( $prefname )
+ or return undef;
+
+ return undef
+ if $pref_row->expiration
+ && $pref_row->expiration < time();
+
+ $pref_row->prefvalue;
+}
+
+=item get_pref_row NAME
+
+Fetch the row object from L<FS::access_user_pref> for prefname NAME
+
+returns undef when no row has been created
+
+=cut
+
+sub get_pref_row {
+ my ( $self, $prefname ) = @_;
+ croak 'prefname parameter required' unless $prefname;
+
+ qsearchs(
+ access_user_pref => {
+ usernum => $self->usernum,
+ prefname => $prefname,
+ }
+ );
+}
+
+=item set_pref NAME, VALUE, [EXPIRATION_EPOCH]
+
+Add or update user preference in L<FS::access_user_pref> table
+
+Passing an undefined VALUE will delete the user preference
+
+Returns VALUE
+
+=cut
+
+sub set_pref {
+ my $self = shift;
+ my ( $prefname, $prefvalue, $expiration ) = @_;
+
+ return $self->delete_pref( $prefname )
+ unless defined $prefvalue;
+
+ if ( my $pref_row = $self->get_pref_row( $prefname )) {
+ return $prefvalue
+ if $pref_row->prefvalue eq $prefvalue;
+
+ $pref_row->prefvalue( $prefvalue );
+ $pref_row->expiration( $expiration || '');
+
+ if ( my $error = $pref_row->replace ) { croak $error }
+
+ return $prefvalue;
+ }
+
+ my $pref_row = FS::access_user_pref->new({
+ usernum => $self->usernum,
+ prefname => $prefname,
+ prefvalue => $prefvalue,
+ expiration => $expiration,
+ });
+ if ( my $error = $pref_row->insert ) { croak $error }
+
+ $prefvalue;
+}
+
+=item delete_pref NAME
+
+Delete user preference from L<FS::access_user_pref> table
+
+=cut
+
+sub delete_pref {
+ my ( $self, $prefname ) = @_;
+
+ my $pref_row = $self->get_pref_row( $prefname )
+ or return;
+
+ if ( my $error = $pref_row->delete ) { croak $error }
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm
index fa047f5..81dfdbc 100644
--- a/FS/FS/contact.pm
+++ b/FS/FS/contact.pm
@@ -199,8 +199,6 @@ sub insert {
}
- $error ||= $self->insert_password_history;
-
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
@@ -302,6 +300,15 @@ sub insert {
}
}
+ if ( $self->get('password') ) {
+ my $error = $self->is_password_allowed($self->get('password'))
+ || $self->change_password($self->get('password'));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -811,7 +818,7 @@ sub authenticate_password {
$hash eq $check_hash;
- } else {
+ } else {
return 0 if $self->_password eq '';
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 47f71c4..7158cb2 100644
--- 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::Misc::Savepoint;
$DEBUG = 0;
$me = '[FS::cust_bill]';
@@ -974,6 +975,9 @@ sub apply_payments_and_credits {
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 }
@@ -1062,6 +1066,7 @@ sub apply_payments_and_credits {
my $error = $app->insert(%options);
if ( $error ) {
+ savepoint_rollback_and_release( $savepoint_label );
$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
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index ea524da..2e8fe81 100644
--- 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::Misc::Savepoint;
# 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
+ # unless exports are suppressed
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;
+ savepoint_create('cancel_pkgs');
+
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) {
- dbh->rollback;
+ savepoint_rollback_and_release('cancel_pkgs');
+ dbh->rollback if $oldAutoCommit;
return ( $error );
}
@@ -2253,11 +2259,13 @@ sub cancel_pkgs {
'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" );
}
}
- 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,
@@ -2271,17 +2279,22 @@ sub cancel_pkgs {
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) {
+ 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 ) {
- dbh->rollback;
+ savepoint_rollback_and_release( $savepoint );
+ dbh->rollback if $oldAutoCommit;
push @errors, $error;
} else {
- dbh->commit;
+ savepoint_release( $savepoint );
+ dbh->commit if $oldAutoCommit;
}
}
if (@errors) {
@@ -2297,8 +2310,11 @@ sub cancel_pkgs {
@cprs = @{ delete $opt{'cust_pkg_reason'} };
}
my $null_reason;
+ $i = 0;
foreach (@pkgs) {
my %lopt = %opt;
+ my $savepoint = 'cancel_pkgs_'.$i++;
+ savepoint_create( $savepoint );
if (@cprs) {
my $cpr = shift @cprs;
if ( $cpr ) {
@@ -2319,10 +2335,12 @@ sub cancel_pkgs {
}
my $error = $_->cancel(%lopt);
if ( $error ) {
- dbh->rollback;
+ savepoint_rollback_and_release( $savepoint );
+ dbh->rollback if $oldAutoCommit;
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
--- 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::Misc::Savepoint;
# 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
- 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 ),
@@ -1961,9 +1965,13 @@ sub do_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;
+ # unless $FS::UID::ForceObeyAutoCommit is set
+ local $FS::UID::AutoCommit = 1
+ unless !$oldAutoCommit
+ && $FS::UID::ForceObeyAutoCommit;
foreach my $cust_event ( @$due_cust_event ) {
@@ -2288,16 +2296,21 @@ sub apply_payments_and_credits {
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 ) {
+ savepoint_rollback_and_release( $savepoint_label );
$dbh->rollback if $oldAutoCommit;
return "Error applying: $error";
}
}
+ savepoint_release( $savepoint_label );
$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
--- 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::Misc::Savepoint;
$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_TIMESTAMP = '';
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)'
- unless $FS::UID::AutoCommit;
+ unless $FS::UID::AutoCommit || $BOP_TESTING;
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},
- '_date' => '',
+ '_date' => $BOP_TESTING ? $BOP_TESTING_TIMESTAMP : '',
'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 ) };
- } 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;
@@ -946,7 +948,7 @@ sub _realtime_bop_result {
'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,
@@ -967,12 +969,16 @@ sub _realtime_bop_result {
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 ) {
- $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'} ?
@@ -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.
- $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".
@@ -996,9 +1003,10 @@ sub _realtime_bop_result {
my $jobnum = $cust_pay_pending->jobnum;
if ( $jobnum ) {
my $placeholder = qsearchs( 'queue', { 'jobnum' => $jobnum } );
-
+
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;
@@ -1008,7 +1016,8 @@ sub _realtime_bop_result {
$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";
@@ -1030,8 +1039,8 @@ sub _realtime_bop_result {
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";
@@ -1039,7 +1048,7 @@ sub _realtime_bop_result {
return $e;
} else {
-
+ savepoint_release( $savepoint_label );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
if ( $options{'apply'} ) {
diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm
index c497059..9d8be12 100644
--- a/FS/FS/cust_payby.pm
+++ b/FS/FS/cust_payby.pm
@@ -1,5 +1,6 @@
package FS::cust_payby;
use base qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
+use feature 'state';
use strict;
use Scalar::Util qw( blessed );
@@ -914,8 +915,81 @@ sub search_sql {
=back
+=item has_autobill_cards
+
+Returns the number of unexpired cards configured for autobill
+
+=cut
+
+sub has_autobill_cards {
+ scalar FS::Record::qsearch({
+ table => 'cust_payby',
+ addl_from => 'JOIN cust_main USING (custnum)',
+ order_by => 'LIMIT 1',
+ hashref => {
+ paydate => { op => '>', value => DateTime->now->ymd },
+ weight => { op => '>', value => 0 },
+ },
+ extra_sql =>
+ "AND payby IN ('CARD', 'DCRD') ".
+ 'AND '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql( table => 'cust_main' ),
+ });
+}
+
+=item has_autobill_checks
+
+Returns the number of check accounts configured for autobill
+
+=cut
+
+sub has_autobill_checks {
+ scalar FS::Record::qsearch({
+ table => 'cust_payby',
+ addl_from => 'JOIN cust_main USING (custnum)',
+ order_by => 'LIMIT 1',
+ hashref => {
+ weight => { op => '>', value => 0 },
+ },
+ extra_sql =>
+ "AND payby IN ('CHEK','DCHEK','DCHK') ".
+ 'AND '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql( table => 'cust_main' ),
+ });
+}
+
+=item future_autobill_report_title
+
+Determine if the future_autobill report should be available.
+If so, return a dynamic title for it
+
=cut
+sub future_autobill_report_title {
+ # Perhaps this function belongs somewhere else
+ state $title;
+ return $title if defined $title;
+
+ # Report incompatible with tax engines
+ return $title = '' if FS::TaxEngine->new->info->{batch};
+
+ my $has_cards = has_autobill_cards();
+ my $has_checks = has_autobill_checks();
+ my $_title = 'Future %s transactions';
+
+ if ( $has_cards && $has_checks ) {
+ $title = sprintf $_title, 'credit card and electronic check';
+ } elsif ( $has_cards ) {
+ $title = sprintf $_title, 'credit card';
+ } elsif ( $has_checks ) {
+ $title = sprintf $_title, 'electronic check';
+ } else {
+ $title = '';
+ }
+
+ $title;
+}
+
sub _upgrade_data {
my $class = shift;
diff --git a/FS/FS/part_export/nena2.pm b/FS/FS/part_export/nena2.pm
index f6a730e..cc4069c 100644
--- 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 Carp qw( carp );
my %upload_targets;
@@ -396,6 +397,13 @@ sub process {
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;
diff --git a/FS/FS/part_export/saisei.pm b/FS/FS/part_export/saisei.pm
index 1b7295d..6db43c1 100644
--- a/FS/FS/part_export/saisei.pm
+++ b/FS/FS/part_export/saisei.pm
@@ -201,12 +201,28 @@ sub _export_insert {
my $accesspoint = process_sector($self, $sector_opt);
return $self->api_error if $self->{'__saisei_error'};
+## get custnum and pkgpart from cust_pkg for virtual access point
+ my $cust_pkg = FS::Record::qsearchs({
+ 'table' => 'cust_pkg',
+ 'hashref' => { 'pkgnum' => $svc_broadband->{Hash}->{pkgnum}, },
+ });
+ my $virtual_ap_name = $cust_pkg->{Hash}->{custnum}.'_'.$cust_pkg->{Hash}->{pkgpart}.'_'.$svc_broadband->{Hash}->{speed_down}.'_'.$svc_broadband->{Hash}->{speed_up};
+
+ my $virtual_ap_opt = {
+ 'virtual_name' => $virtual_ap_name,
+ 'sector_name' => $sector_name,
+ 'virtual_uprate_limit' => $svc_broadband->{Hash}->{speed_up},
+ 'virtual_downrate_limit' => $svc_broadband->{Hash}->{speed_down},
+ };
+ my $virtual_ap = process_virtual_ap($self, $virtual_ap_opt);
+ return $self->api_error if $self->{'__saisei_error'};
+
## tie host to user add sector name as access point.
$self->api_add_host_to_user(
$user->{collection}->[0]->{name},
$rateplan->{collection}->[0]->{name},
$svc_broadband->{Hash}->{ip_addr},
- $accesspoint->{collection}->[0]->{name},
+ $virtual_ap->{collection}->[0]->{name},
) unless $self->{'__saisei_error'};
}
@@ -216,8 +232,8 @@ sub _export_insert {
sub _export_replace {
my ($self, $svc_broadband) = @_;
- $self->_export_insert($svc_broadband);
- return '';
+ my $error = $self->_export_insert($svc_broadband);
+ return $error;
}
sub _export_delete {
@@ -817,6 +833,44 @@ sub process_sector {
return $accesspoint;
}
+sub process_virtual_ap {
+ my ($self, $opt) = @_;
+
+ my $existing_virtual_ap;
+ my $virtual_name = $opt->{virtual_name};
+
+ #check if sector has been set up as an access point.
+ $existing_virtual_ap = $self->api_get_accesspoint($virtual_name);
+
+ # modify the existing virtual accesspoint if changing it. this should never happen
+ $self->api_modify_existing_accesspoint (
+ $virtual_name,
+ $opt->{sector_name},
+ $opt->{virtual_uprate_limit},
+ $opt->{virtual_downrate_limit},
+ ) if $existing_virtual_ap && $opt->{modify_existing};
+
+ #if virtual ap does not exist as an access point create it.
+ $self->api_create_accesspoint(
+ $virtual_name,
+ $opt->{virtual_uprate_limit},
+ $opt->{virtual_downrate_limit},
+ ) unless $existing_virtual_ap;
+
+my $update_sector;
+if ($existing_virtual_ap && ($existing_virtual_ap->{collection}->[0]->{uplink}->{link}->{name} ne $opt->{sector_name})) {
+ $update_sector = 1;
+}
+
+ # Attach newly created virtual ap to tower sector ap or if sector has changed.
+ $self->api_modify_accesspoint($virtual_name, $opt->{sector_name}) unless ($self->{'__saisei_error'} || ($existing_virtual_ap && !$update_sector));
+
+ # set access point to existing one or newly created one.
+ my $accesspoint = $existing_virtual_ap ? $existing_virtual_ap : $self->api_get_accesspoint($virtual_name);
+
+ return $accesspoint;
+}
+
sub export_provisioned_services {
my $job = shift;
my $param = shift;