X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FTaxEngine%2Fsuretax.pm;h=356f5f318cacef91db2a45469b12dc7c1767c0dc;hb=368ed08e24400e9d1faf401a1e4e23ea54d2c969;hp=327a72843caf7d3d7d7971ff0b4e8b76a231eb05;hpb=817c1ce0e1cbcfd1f684222c66f46dd13b2d6dd7;p=freeside.git diff --git a/FS/FS/TaxEngine/suretax.pm b/FS/FS/TaxEngine/suretax.pm index 327a72843..356f5f318 100644 --- a/FS/FS/TaxEngine/suretax.pm +++ b/FS/FS/TaxEngine/suretax.pm @@ -4,7 +4,7 @@ use strict; use base 'FS::TaxEngine'; use FS::Conf; use FS::Record qw(qsearch qsearchs dbh); -use JSON; +use Cpanel::JSON::XS; use XML::Simple qw(XMLin); use LWP::UserAgent; use HTTP::Request::Common; @@ -14,15 +14,12 @@ our $DEBUG = 1; # prints progress messages # $DEBUG = 2; # prints decoded request and response (noisy, be careful) # $DEBUG = 3; # prints raw response from the API, ridiculously unreadable -our $json = JSON->new->pretty(1); +our $json = Cpanel::JSON::XS->new->pretty(0)->shrink(1); our %taxproduct_cache; our $conf; -our $host = 'testapi.taxrating.net'; -# production: 'api.taxrating.net' - FS::UID->install_callback( sub { $conf = FS::Conf->new; # should we enable conf caching here? @@ -80,7 +77,7 @@ sub build_request { ($self->{bill_zip}, $self->{bill_plus4}) = split('-', $cust_main->bill_location->zip); - $self->{regcode} = $REGCODE{ $conf->config('suretax-regulatory_code') }; + $self->{regcode} = $REGCODE{ $conf->config('suretax-regulatory_code', $agentnum) }; %taxproduct_cache = (); @@ -88,6 +85,8 @@ sub build_request { my @lines = map { $self->build_item($_) } $cust_bill->cust_bill_pkg; + return if !@lines; + my $ClientNumber = $conf->config('suretax-client_number') or die "suretax-client_number config required.\n"; my $ValidationKey = $conf->config('suretax-validation_key') @@ -98,8 +97,8 @@ sub build_request { ClientNumber => $ClientNumber, ValidationKey => $ValidationKey, BusinessUnit => $BusinessUnit, - DataYear => '2015', #$date->year, - DataMonth => '04', #sprintf('%02d', $date->month), + DataYear => $date->year, + DataMonth => sprintf('%02d', $date->month), TotalRevenue => sprintf('%.4f', $cust_bill->charged), ReturnFileCode => ($self->{estimate} ? 'Q' : '0'), ClientTracking => $cust_bill->invnum, @@ -138,8 +137,17 @@ sub build_item { my @items; my $recur_without_usage = $cust_bill_pkg->recur; + # use the _configured_ tax location as 'Zipcode' (respecting + # tax-ship_address and tax-pkg_address configs) my $location = $cust_bill_pkg->tax_location; - my ($svc_zip, $svc_plus4) = split('-', $location->zip); + my ($zip, $plus4) = split('-', $location->zip); + + # and the _real_ location as 'P2PZipcode' + my $svc_location = $location; + if ( $cust_bill_pkg->pkgnum ) { + $svc_location = $cust_bill_pkg->cust_pkg->cust_location; + } + my ($svc_zip, $svc_plus4) = split('-', $svc_location->zip); my $startdate = DateTime->from_epoch( epoch => $cust_bill->_date )->strftime('%m-%d-%Y'); @@ -151,8 +159,8 @@ sub build_item { 'OrigNumber' => '', 'TermNumber' => '', 'BillToNumber' => '', - 'Zipcode' => $self->{bill_zip}, - 'Plus4' => ($self->{bill_plus4} ||= '0000'), + 'Zipcode' => $zip, + 'Plus4' => ($plus4 ||= '0000'), 'P2PZipcode' => $svc_zip, 'P2PPlus4' => ($svc_plus4 ||= '0000'), # we don't support Order Placement/Approval zip codes @@ -205,27 +213,31 @@ sub build_item { while ( my $cdr = $cdrs->fetch ) { my $calldate = DateTime->from_epoch( epoch => $cdr->startdate )->strftime('%m-%d-%Y'); - # determine the tax situs rule; it's different (probably more accurate) - # if the call has PSTN phone numbers at both ends - my $tsr = $TSR_CALL_OTHER; - if ( $cdr->charged_party =~ /^\d{10}$/ and - $cdr->src =~ /^\d{10}$/ and - $cdr->dst =~ /^\d{10}$/ ) { - $tsr = $TSR_CALL_NPANXX; - } my %hash = ( %base_item, 'LineNumber' => 'C' . $cdr->acctid, - 'OrigNumber' => $cdr->src, - 'TermNumber' => $cdr->dst, - 'BillToNumber' => $cdr->charged_party, + 'OrigNumber' => '', + 'TermNumber' => '', + 'BillToNumber' => '', 'TransDate' => $calldate, 'Revenue' => $cdr->rated_price, # 4 decimal places 'Units' => 0, # right? 'CallDuration' => $cdr->duration, - 'TaxSitusRule' => $tsr, + 'TaxSitusRule' => $TSR_CALL_OTHER, 'TransTypeCode' => $taxproduct, ); + # determine the tax situs rule; it's different (probably more accurate) + # if the call has PSTN phone numbers at both ends + if ( $cdr->charged_party =~ /^\d{10}$/ and + $cdr->src =~ /^\d{10}$/ and + $cdr->dst =~ /^\d{10}$/ and + !$cdr->is_tollfree ) { + $hash{TaxSitusRule} = $TSR_CALL_NPANXX; + $hash{OrigNumber} = $cdr->src; + $hash{TermNumber} = $cdr->dst; + $hash{BillToNumber} = $cdr->charged_party; + } + push @items, \%hash; } # while ($cdrs->fetch) @@ -246,11 +258,13 @@ sub build_item { if !$taxproduct; my $tsr = $TSR_GENERAL; + # when billing on cancellation there are no units + my $units = $self->{cancel} ? 0 : $cust_bill_pkg->units; my %hash = ( %base_item, 'LineNumber' => 'R' . $billpkgnum, 'Revenue' => $recur_without_usage, # 4 decimal places - 'Units' => $cust_bill_pkg->units, + 'Units' => $units, 'TaxSitusRule' => $tsr, 'TransTypeCode' => $taxproduct, ); @@ -309,11 +323,19 @@ sub make_taxlines { # assemble the request hash my $request = $self->build_request; + if (!$request) { + warn "no taxable items in invoice; skipping SureTax request\n" if $DEBUG; + return; + } - warn "sending SureTax request\n" if $DEBUG; + warn "encoding SureTax request\n" if $DEBUG; my $request_json = $json->encode($request); warn $request_json if $DEBUG > 1; + my $host = $conf->config('suretax-hostname'); + $host ||= 'testapi.taxrating.net'; + + warn "sending SureTax request\n" if $DEBUG; # We are targeting the "V05" interface: # - accepts both telecom and general sales transactions # - produces results broken down by "invoice" (Freeside line item) @@ -325,8 +347,11 @@ sub make_taxlines { 'Accept' => 'application/json', ); + warn 'received SureTax response: '. $http_response->status_line. "\n" + if $DEBUG; + die $http_response->status_line. "\n" unless $http_response->is_success; + my $raw_response = $http_response->content; - warn "received response\n" if $DEBUG; warn $raw_response if $DEBUG > 2; my $response; if ( $raw_response =~ /^<\?xml/ ) { @@ -335,8 +360,10 @@ sub make_taxlines { $response = XMLin( $raw_response ); $raw_response = $response->{content}; } + + warn "decoding SureTax response\n" if $DEBUG; $response = eval { $json->decode($raw_response) } - or die "$raw_response\n"; + or die "Can't JSON-decode response: $raw_response\n"; # documentation implies this might be necessary $response = $response->{'d'} if exists $response->{'d'}; @@ -354,6 +381,7 @@ sub make_taxlines { } return if !$response->{GroupList}; + warn "creating FS objects from SureTax data\n" if $DEBUG; foreach my $taxable ( @{ $response->{GroupList} } ) { # each member of this array here corresponds to what SureTax calls an # "invoice" and we call a "line item". The invoice number is @@ -399,6 +427,7 @@ sub make_taxlines { }); } } + warn "TaxEngine/suretax.pm make_taxlines done; returning FS objects\n" if $DEBUG; return @elements; }