From: Ivan Kohler Date: Tue, 1 Nov 2016 23:20:44 +0000 (-0700) Subject: Merge branch 'master' of git.freeside.biz:/home/git/freeside X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=f2b43a877c70aa367595fe2fc4fcffd82f62d001;hp=bd29d65b7ec7b2637656fbc66ae0f57fa02dcbce Merge branch 'master' of git.freeside.biz:/home/git/freeside --- diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index 7c17ae39e..091d6ac68 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -1022,7 +1022,7 @@ sub validate_payment { validate($payinfo) or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo return { 'error' => gettext('unknown_card_type') } - if $payinfo !~ /^99\d{14}$/ && cardtype($payinfo) eq "Unknown"; + if !$cust_main->tokenized($payinfo) && cardtype($payinfo) eq "Unknown"; if ( length($p->{'paycvv'}) && $p->{'paycvv'} !~ /^\s*$/ ) { if ( cardtype($payinfo) eq 'American Express card' ) { diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 9c8e37499..2136ad285 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -1895,7 +1895,7 @@ sub check_payinfo_cardtype { my $payinfo = $self->payinfo; $payinfo =~ s/\D//g; - return '' if $payinfo =~ /^99\d{14}$/; #token + return '' if $self->tokenized($payinfo); #token my %bop_card_types = map { $_=>1 } values %{ card_types() }; my $cardtype = cardtype($payinfo); diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index 0b6b09981..7718f7aab 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -376,14 +376,18 @@ sub _bop_content { } sub _tokenize_card { - my ($self,$transaction,$cust_payby,$log,%opt) = @_; + my ($self,$transaction,$options,$log,%opt) = @_; + # options is for entire process, so we can update payinfo + # opt is just for this call, only key is replace + my $cust_payby = $options->{'cust_payby'}; if ( $cust_payby and $transaction->can('card_token') and $transaction->card_token - and $cust_payby->payinfo !~ /^99\d{14}$/ #not already tokenized + and !$cust_payby->tokenized #not already tokenized ) { + $options->{'payinfo'} = $transaction->card_token; $cust_payby->payinfo($transaction->card_token); my $error; @@ -400,6 +404,18 @@ sub _tokenize_card { } +# only store payinfo in cust_pay/cust_pay_pending +# if it's a tokenized card or if processor requires card for void +sub _cust_pay_opts { + my ($self,$payby,$payinfo,$transaction) = @_; + ( (($payby eq 'CARD') && $self->tokenized($payinfo)) + || (($payby eq 'CARD') && $transaction->info('CC_void_requires_card')) + || (($payby eq 'CHEK') && $transaction->info('ECHECK_void_requires_account')) + ) + ? ('payinfo' => $payinfo) + : (); +} + my %bop_method2payby = ( 'CC' => 'CARD', 'ECHECK' => 'CHEK', @@ -665,12 +681,15 @@ sub realtime_bop { #okay, good to go, if we're a duplicate, cust_pay_pending will kick us out + my $transaction = new $namespace( $payment_gateway->gateway_module, + $self->_bop_options(\%options), + ); + my $cust_pay_pending = new FS::cust_pay_pending { 'custnum' => $self->custnum, 'paid' => $options{amount}, '_date' => '', 'payby' => $bop_method2payby{$options{method}}, - 'payinfo' => $options{payinfo}, 'paymask' => $options{paymask}, 'paydate' => $paydate, 'recurring_billing' => $content{recurring_billing}, @@ -679,6 +698,7 @@ sub realtime_bop { 'gatewaynum' => $payment_gateway->gatewaynum || '', 'session_id' => $options{session_id} || '', 'jobnum' => $options{depend_jobnum} || '', + $self->_cust_pay_opts($options{payinfo},$transaction), }; $cust_pay_pending->payunique( $options{payunique} ) if defined($options{payunique}) && length($options{payunique}); @@ -695,10 +715,6 @@ sub realtime_bop { my( $action1, $action2 ) = split( /\s*\,\s*/, $payment_gateway->gateway_action ); - my $transaction = new $namespace( $payment_gateway->gateway_module, - $self->_bop_options(\%options), - ); - $transaction->content( 'type' => $options{method}, $self->_bop_auth(\%options), @@ -811,7 +827,7 @@ sub realtime_bop { # Tokenize ### - my $error = $self->_tokenize_card($transaction,$options{'cust_payby'},$log,'replace' => 1); + my $error = $self->_tokenize_card($transaction,\%options,$log,'replace' => 1); return $error if $error; ### @@ -849,9 +865,7 @@ sub fake_bop { 'paid' => $options{amount}, '_date' => '', 'payby' => $bop_method2payby{$options{method}}, - #'payinfo' => $payinfo, 'payinfo' => '4111111111111111', - #'paydate' => $paydate, 'paydate' => '2012-05-01', 'processor' => 'FakeProcessor', 'auth' => '54', @@ -925,7 +939,6 @@ sub _realtime_bop_result { 'paid' => $cust_pay_pending->paid, '_date' => '', 'payby' => $cust_pay_pending->payby, - 'payinfo' => $options{'payinfo'}, 'paymask' => $options{'paymask'} || $cust_pay_pending->paymask, 'paydate' => $cust_pay_pending->paydate, 'pkgnum' => $cust_pay_pending->pkgnum, @@ -935,6 +948,7 @@ sub _realtime_bop_result { 'auth' => $transaction->authorization, 'order_number' => $order_number || '', 'no_auto_apply' => $options{'no_auto_apply'} ? 'Y' : '', + $self->_cust_pay_opts($options{payinfo},$transaction), } ); #doesn't hurt to know, even though the dup check is in cust_pay_pending now $cust_pay->payunique( $options{payunique} ) @@ -1840,7 +1854,9 @@ sub realtime_verify_bop { ### my $error; - my $transaction; #need this back so we can do _tokenize_card + my $transaction = new $namespace( $payment_gateway->gateway_module, + $self->_bop_options(\%options), + ); #need this back so we can do _tokenize_card # don't mutex the customer here, because they might be uncommitted. and # this is only verification. it doesn't matter if they have other @@ -1851,13 +1867,13 @@ sub realtime_verify_bop { 'paid' => '1.00', '_date' => '', 'payby' => $bop_method2payby{'CC'}, - 'payinfo' => $options{payinfo}, 'paymask' => $options{paymask}, 'paydate' => $paydate, 'pkgnum' => $options{'pkgnum'}, 'status' => 'new', 'gatewaynum' => $payment_gateway->gatewaynum || '', 'session_id' => $options{session_id} || '', + $self->_cust_pay_opts($options{payinfo},$transaction), }; $cust_pay_pending->payunique( $options{payunique} ) if defined($options{payunique}) && length($options{payunique}); @@ -1888,10 +1904,6 @@ sub realtime_verify_bop { if $DEBUG > 1; warn Dumper($cust_pay_pending) if $DEBUG > 2; - $transaction = new $namespace( $payment_gateway->gateway_module, - $self->_bop_options(\%options), - ); - $transaction->content( 'type' => 'CC', $self->_bop_auth(\%options), @@ -2115,7 +2127,7 @@ sub realtime_verify_bop { #important that we not pass replace option here, #because cust_payby->replace uses realtime_verify_bop! - $self->_tokenize_card($transaction,$options{'cust_payby'},$log); + $self->_tokenize_card($transaction,\%options,$log); ### # result handling @@ -2162,7 +2174,7 @@ sub realtime_tokenize { return "No cust_payby" unless $options{'cust_payby'}; $self->_bop_cust_payby_options(\%options); return '' unless $options{method} eq 'CC'; - return '' if $options{payinfo} =~ /^99\d{14}$/; #already tokenized + return '' if $self->tokenized($options{payinfo}); #already tokenized ### # select a gateway @@ -2254,7 +2266,7 @@ sub realtime_tokenize { #important that we not pass replace option here, #because cust_payby->replace uses realtime_tokenize! - $self->_tokenize_card($transaction,$options{'cust_payby'},$log); + $self->_tokenize_card($transaction,\%options,$log); } else { @@ -2266,6 +2278,12 @@ sub realtime_tokenize { } +sub tokenized { + my $this = shift; + my $payinfo = shift; + $payinfo =~ /^99\d{14}$/; +} + =back =head1 BUGS diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm index a1233d083..65fb72208 100644 --- a/FS/FS/cust_main_county.pm +++ b/FS/FS/cust_main_county.pm @@ -682,6 +682,37 @@ END } +sub _merge_into { + # for internal use: takes another cust_main_county object, transfers + # all existing references to this record to that one, and deletes this + # one. + my $record = shift; + my $other = shift or die "record to merge into must be provided"; + my $new_taxnum = $other->taxnum; + my $old_taxnum = $record->taxnum; + if ($other->tax != $record->tax or + $other->exempt_amount != $record->exempt_amount) { + # don't assume these are the same. + warn "Found duplicate taxes (#$new_taxnum and #$old_taxnum) but they have different rates and can't be merged.\n"; + } else { + warn "Merging tax #$old_taxnum into #$new_taxnum\n"; + foreach my $table (qw( + cust_bill_pkg_tax_location + cust_bill_pkg_tax_location_void + cust_tax_exempt_pkg + cust_tax_exempt_pkg_void + )) { + foreach my $row (qsearch($table, { 'taxnum' => $old_taxnum })) { + $row->set('taxnum' => $new_taxnum); + my $error = $row->replace; + die $error if $error; + } + } + my $error = $record->delete; + die $error if $error; + } +} + sub _upgrade_data { my $class = shift; # assume taxes in Washington with district numbers, and null name, or @@ -704,6 +735,28 @@ sub _upgrade_data { } FS::upgrade_journal->set_done($journal); } + my @key_fields = (qw(city county state country district taxname taxclass)); + + # remove duplicates (except disabled records) + my @duplicate_sets = qsearch({ + table => 'cust_main_county', + select => FS::Record::group_concat_sql('taxnum', ',') . ' AS taxnums, ' . + join(',', @key_fields), + extra_sql => ' WHERE tax > 0 + GROUP BY city, county, state, country, district, taxname, taxclass + HAVING COUNT(*) > 1' + }); + warn "Found ".scalar(@duplicate_sets)." set(s) of duplicate tax definitions\n" + if @duplicate_sets; + foreach my $set (@duplicate_sets) { + my @taxnums = split(',', $set->get('taxnums')); + my $first = FS::cust_main_county->by_key(shift @taxnums); + foreach my $taxnum (@taxnums) { + my $record = FS::cust_main_county->by_key($taxnum); + $record->_merge_into($first); + } + } + # trim whitespace and convert to uppercase in the 'city' field. foreach my $record (qsearch({ table => 'cust_main_county', @@ -714,33 +767,10 @@ sub _upgrade_data { # create an exact duplicate. # so find the record this one would duplicate, and merge them. $record->check; # trims whitespace - my %match = map { $_ => $record->get($_) } - qw(city county state country district taxname taxclass); + my %match = map { $_ => $record->get($_) } @key_fields; my $other = qsearchs('cust_main_county', \%match); if ($other) { - my $new_taxnum = $other->taxnum; - my $old_taxnum = $record->taxnum; - if ($other->tax != $record->tax or - $other->exempt_amount != $record->exempt_amount) { - # don't assume these are the same. - warn "Found duplicate taxes (#$new_taxnum and #$old_taxnum) but they have different rates and can't be merged.\n"; - } else { - warn "Merging tax #$old_taxnum into #$new_taxnum\n"; - foreach my $table (qw( - cust_bill_pkg_tax_location - cust_bill_pkg_tax_location_void - cust_tax_exempt_pkg - cust_tax_exempt_pkg_void - )) { - foreach my $row (qsearch($table, { 'taxnum' => $old_taxnum })) { - $row->set('taxnum' => $new_taxnum); - my $error = $row->replace; - die $error if $error; - } - } - my $error = $record->delete; - die $error if $error; - } + $record->_merge_into($other); } else { # else there is no record this one duplicates, so just fix it my $error = $record->replace; diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm index 626fc9fe9..53608cf64 100644 --- a/FS/FS/cust_payby.pm +++ b/FS/FS/cust_payby.pm @@ -276,7 +276,7 @@ sub replace { if ( $self->payby =~ /^(CARD|CHEK)$/ && ( ( $self->get('payinfo') ne $old->get('payinfo') - && $self->get('payinfo') !~ /^99\d{14}$/ + && !$self->tokenized ) || grep { $self->get($_) ne $old->get($_) } qw(paydate payname) ) @@ -357,7 +357,7 @@ sub check { or return gettext('invalid_card'); # . ": ". $self->payinfo; my $cardtype = cardtype($payinfo); - $cardtype = 'Tokenized' if $self->payinfo =~ /^99\d{14}$/; #token + $cardtype = 'Tokenized' if $self->tokenized; #token return gettext('unknown_card_type') if $cardtype eq "Unknown"; @@ -546,7 +546,7 @@ sub check_payinfo_cardtype { my $payinfo = $self->payinfo; $payinfo =~ s/\D//g; - if ( $payinfo =~ /^99\d{14}$/ ) { + if ( $self->tokenized($payinfo) ) { $self->set('paycardtype', 'Tokenized'); return ''; } diff --git a/FS/FS/geocode_Mixin.pm b/FS/FS/geocode_Mixin.pm index a372faaa8..09b113112 100644 --- a/FS/FS/geocode_Mixin.pm +++ b/FS/FS/geocode_Mixin.pm @@ -273,7 +273,7 @@ sub process_district_update { my $error = $self->replace; die $error if $error; - my %hash = map { $_ => $tax_info->{$_} } + my %hash = map { $_ => uc( $tax_info->{$_} ) } qw( district city county state country ); $hash{'source'} = $method; # apply the update only to taxes we maintain diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 008ba8a86..35f178e25 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -1917,13 +1917,27 @@ sub calc_remain { 0; } =item calc_units CUST_PKG This returns the number of provisioned svc_phone records, or, of the package -count_available_phones option is set, the number available to be provisoined +count_available_phones option is set, the number available to be provisioned in the package. =cut -#fallback that returns 0 for old legacy packages with no plan -sub calc_units { 0; } +sub calc_units { + my($self, $cust_pkg ) = @_; + my $count = 0; + if ( $self->option('count_available_phones', 1)) { + foreach my $pkg_svc ($cust_pkg->part_pkg->pkg_svc) { + if ($pkg_svc->part_svc->svcdb eq 'svc_phone') { # svc_pbx? + $count += $pkg_svc->quantity || 0; + } + } + $count *= $cust_pkg->quantity; + } else { + $count = + scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc); + } + $count; +} #fallback for everything not based on flat.pm sub recur_temporality { 'upcoming'; } diff --git a/FS/FS/part_pkg/global_Mixin.pm b/FS/FS/part_pkg/global_Mixin.pm index e82602e1a..59eaaaaec 100644 --- a/FS/FS/part_pkg/global_Mixin.pm +++ b/FS/FS/part_pkg/global_Mixin.pm @@ -30,6 +30,10 @@ sub validate_moneyn { return ''; } +tie my %count_available_phones, 'Tie::IxHash', ( + 0 => 'Provisioned phone services', + 1 => 'All available phone services', +); %info = ( 'disabled' => 1, @@ -63,6 +67,11 @@ sub validate_moneyn { 'name' => 'Automatic suspension period before cancelling (configuration setting part_pkg-delay_cancel-days)', 'type' => 'checkbox', }, + 'count_available_phones' => { 'name' => 'Count taxable phone lines', + 'type' => 'radio', + 'options' => \%count_available_phones, + 'default' => 0, + }, # miscellany--maybe put this in a separate module? @@ -134,6 +143,8 @@ sub validate_moneyn { unused_credit_change delay_cancel + count_available_phones + a2billing_tariff a2billing_type a2billing_simultaccess diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm index 420026dcb..9ecdba685 100644 --- a/FS/FS/part_pkg/voip_cdr.pm +++ b/FS/FS/part_pkg/voip_cdr.pm @@ -289,10 +289,6 @@ tie my %accountcode_tollfree_field, 'Tie::IxHash', 'type' => 'checkbox', }, - 'count_available_phones' => { 'name' => 'Consider for tax purposes the number of lines to be svc_phones that may be provisioned rather than those that actually are.', - 'type' => 'checkbox', - }, - #XXX also have option for an external db? these days we suck them into ours # 'cdr_location' => { 'name' => 'CDR database location' # 'type' => 'select', @@ -353,7 +349,7 @@ tie my %accountcode_tollfree_field, 'Tie::IxHash', usage_mandate usage_section summarize_usage usage_showzero bill_every_call bill_inactive_svcs bill_only_pkg_dates - count_available_phones suspend_bill + suspend_bill ) ], 'weight' => 41, @@ -656,25 +652,6 @@ sub is_free { 0; } -# This equates svc_phone records; perhaps svc_phone should have a field -# to indicate it represents a line -sub calc_units { - my($self, $cust_pkg ) = @_; - my $count = 0; - if ( $self->option('count_available_phones', 1)) { - foreach my $pkg_svc ($cust_pkg->part_pkg->pkg_svc) { - if ($pkg_svc->part_svc->svcdb eq 'svc_phone') { # svc_pbx? - $count += $pkg_svc->quantity || 0; - } - } - $count *= $cust_pkg->quantity; - } else { - $count = - scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc); - } - $count; -} - sub reset_usage { my ($self, $cust_pkg, %opt) = @_; my @part_pkg_usage = $self->part_pkg_usage or return ''; diff --git a/FS/FS/part_pkg/voip_inbound.pm b/FS/FS/part_pkg/voip_inbound.pm index e911439c8..15af706c1 100644 --- a/FS/FS/part_pkg/voip_inbound.pm +++ b/FS/FS/part_pkg/voip_inbound.pm @@ -399,15 +399,5 @@ sub is_free { 0; } -# This equates svc_phone records; perhaps svc_phone should have a field -# to indicate it represents a line -# #XXX no count_available_phones? -sub calc_units { - my($self, $cust_pkg ) = @_; - my $count = - scalar(grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc); - $count; -} - 1; diff --git a/FS/FS/payinfo_Mixin.pm b/FS/FS/payinfo_Mixin.pm index 3a32ad5b2..a0a2cbcc9 100644 --- a/FS/FS/payinfo_Mixin.pm +++ b/FS/FS/payinfo_Mixin.pm @@ -67,9 +67,9 @@ sub payinfo { my($self,$payinfo) = @_; if ( defined($payinfo) ) { - $self->paymask($self->mask_payinfo) unless $self->payinfo =~ /^99\d{14}$/; #make sure old mask is set + $self->paymask($self->mask_payinfo) unless $self->tokenized; #make sure old mask is set $self->setfield('payinfo', $payinfo); - $self->paymask($self->mask_payinfo) unless $payinfo =~ /^99\d{14}$/; #remask unless tokenizing + $self->paymask($self->mask_payinfo) unless $self->tokenized($payinfo); #remask unless tokenizing } else { $self->getfield('payinfo'); } @@ -130,7 +130,7 @@ sub mask_payinfo { # Check to see if it's encrypted... if ( ref($self) && $self->is_encrypted($payinfo) ) { return 'N/A'; - } elsif ( $payinfo =~ /^99\d{14}$/ || $payinfo eq 'N/A' ) { #token + } elsif ( $self->tokenized($payinfo) || $payinfo eq 'N/A' ) { #token return 'N/A (tokenized)'; #? } else { # if not, mask it... @@ -198,7 +198,7 @@ sub payinfo_check { my $payinfo = $self->payinfo; my $cardtype = cardtype($payinfo); - $cardtype = 'Tokenized' if $payinfo =~ /^99\d{14}$/; + $cardtype = 'Tokenized' if $self->tokenized; $self->set('paycardtype', $cardtype); if ( $ignore_masked_payinfo and $self->mask_payinfo eq $self->payinfo ) { @@ -233,6 +233,7 @@ sub payinfo_check { } } + return ''; } =item payby_payinfo_pretty [ LOCALE ] @@ -453,6 +454,15 @@ sub process_set_cardtype { } } +sub tokenized { + my $self = shift; + my $payinfo = scalar(@_) ? shift : $self->payinfo; + ## or just $self->cust_main->tokenized($payinfo) ?? + ## everything that currently uses this mixin is linked to cust_main, + ## but just in case, false laziness w/ FS::cust_main::Billing_Realtime + $payinfo =~ /^99\d{14}$/; +} + =back =head1 BUGS diff --git a/bin/wa_tax_rate_update b/bin/wa_tax_rate_update index 2d493db30..fbca9dd7d 100644 --- a/bin/wa_tax_rate_update +++ b/bin/wa_tax_rate_update @@ -78,6 +78,7 @@ my $total_skipped = 0; while ( !$csv->eof ) { my $line = $csv->getline_hr($fh); my $district = $line->{Code} or next; + $district = sprintf('%04d', $district); my $tax = sprintf('%.1f', $line->{Rate} * 100); my $changed = 0; my $skipped = 0; diff --git a/httemplate/browse/cust_main_county.cgi b/httemplate/browse/cust_main_county.cgi index 522614886..26a3e21b9 100755 --- a/httemplate/browse/cust_main_county.cgi +++ b/httemplate/browse/cust_main_county.cgi @@ -467,13 +467,13 @@ my @fields = ( ? ' '. add_link( desc => 'Add more counties', col => 'state', - label=> 'add more counties', + label=> 'add more counties', row => $_[0], cgi => $cgi, ). ' '. collapse_link( col => 'state', - label=> 'remove all counties', + label=> 'remove all counties', row => $_[0], cgi => $cgi, ) @@ -484,7 +484,7 @@ my @fields = ( ? '' : ' '. expand_link( desc => 'Add States', row => $_[0], - label => 'add states', + label => 'add states', cgi => $cgi, ) ); @@ -503,18 +503,18 @@ my @fields = ( ? ' '. add_link( desc => 'Add more cities', col => 'county', - label=> 'add more cities', + label=> 'add more cities', row => $_[0], cgi => $cgi, ). ' '. collapse_link( col => 'county', - label=> 'remove all cities', + label=> 'remove all cities', row => $_[0], cgi => $cgi, ) : ' '. remove_link( col => 'county', - label=> 'remove county', + label=> 'remove county', row => $_[0], cgi => $cgi, ); @@ -525,7 +525,7 @@ my @fields = ( : '(all) '. expand_link( desc => 'Add Counties', row => $_[0], - label => 'add counties', + label => 'add counties', cgi => $cgi, ); }, @@ -541,7 +541,7 @@ my @fields = ( } else { $r->city. ' '. remove_link( col => 'city', - label=> 'remove city', + label=> 'remove city', row => $r, cgi => $cgi, ); @@ -550,7 +550,7 @@ my @fields = ( '(all) '. expand_link( desc => 'Add Cities', row => $r, - label => 'add cities', + label => 'add cities', cgi => $cgi, ); } @@ -562,7 +562,7 @@ my @fields = ( if ( $r->district ) { $r->district . ' '. remove_link( col => 'district', - label=> 'remove district', + label=> 'remove district', row => $r, cgi => $cgi, ); diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi index 74ca7348f..84687f019 100644 --- a/httemplate/misc/process/payment.cgi +++ b/httemplate/misc/process/payment.cgi @@ -135,7 +135,7 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) { validate($payinfo) or errorpage(gettext('invalid_card')); - unless ( $payinfo =~ /^99\d{14}$/ ) { #token + unless ( $cust_main->tokenized($payinfo) ) { #token my $cardtype = cardtype($payinfo); diff --git a/httemplate/search/sqlradius_usage.html b/httemplate/search/sqlradius_usage.html index 846b50c2c..b810a8bf6 100644 --- a/httemplate/search/sqlradius_usage.html +++ b/httemplate/search/sqlradius_usage.html @@ -5,8 +5,8 @@ % $cgi->param('agentnum', $agent->agentnum); #for download links
<% $agent->agent %>

- <& cust_pkg_sqlradius_usage.html, - exports => @exports, + <& sqlradius_usage.html, + exports => \@exports, agentnum => $agent->agentnum, nohtmlheader => 1, download_label => 'Download this section', @@ -154,6 +154,17 @@ my %usage_param = ( my @total_usage = ('', 0, 0, 0); # username, input, output, input + output +# remember which exports apply to which services, so we don't inappropriately +# ask the wrong ones for usage stats +my %export_svcparts; +foreach my $export (@exports) { + my %seen; + foreach ($export->export_svc) { + $seen{ $_->svcpart } = 1; + } + $export_svcparts{ $export->exportnum } = \%seen; +} + # a single sub to collect data for each package, aggregated across both # services and exports. when we add per-service breakdown, this should also # keep the per-service data, but not needed yet @@ -163,16 +174,20 @@ my $cust_pkg_stats_sub = sub { my ($upbytes, $downbytes, $totalbytes) = (0, 0, 0); my $display_username; foreach my $svcnum ( split(',', $cust_pkg->get('svcnums_concat')) ) { + my $cust_svc = FS::cust_svc->by_key($svcnum); + my $svc = $cust_svc->svc_x; foreach my $export (@exports) { - my $svc = FS::cust_svc->by_key($svcnum)->svc_x; - my $username = $export->export_username($svc); - my $usage = $export->usage_sessions({ %usage_param, 'svc' => $svc }); - # returns arrayref with one row - $upbytes += $usage->[0]->{'acctinputoctets'}; - $downbytes += $usage->[0]->{'acctoutputoctets'}; - # in combined services mode with multiple users/MAC addresses per - # package, this will just show one of them arbitrarily. - $display_username ||= $username; + if ( $export_svcparts{ $export->exportnum }{ $cust_svc->svcpart } ) { + my $username = $export->export_username($svc); + my $usage = $export->usage_sessions({ %usage_param, 'svc' => $svc }); + # returns arrayref with one row + $upbytes += $usage->[0]->{'acctinputoctets'}; + $downbytes += $usage->[0]->{'acctoutputoctets'}; + # in combined services mode with multiple users/MAC addresses per + # package, this will just show one of them arbitrarily. + $display_username ||= $username; + } + # else this export doesn't apply so skip it } } $total_usage[1] += $upbytes;