summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2016-12-07 15:27:49 -0800
committerMark Wells <mark@freeside.biz>2016-12-07 15:27:58 -0800
commit7a33cb6e4c3e33b7399d6574cbd3ee38ddcba5e0 (patch)
treeb46feaff7d6c842e2ee3be38d71b684d33d7b7f2 /FS
parentecd038f7ae5c1ffc929f3c928ecd161eeb45d9be (diff)
specify Avalara tax product for per-line taxes, #73063
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Schema.pm5
-rw-r--r--FS/FS/TaxEngine/billsoft.pm54
-rw-r--r--FS/FS/part_pkg.pm14
-rw-r--r--FS/FS/part_pkg_taxproduct.pm32
4 files changed, 65 insertions, 40 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index f8b82f4..0e41b1a 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3248,6 +3248,7 @@ sub tables_hashref {
'adjourn_months', 'int', 'NULL', '', '', '',
'contract_end_months','int','NULL', '', '', '',
'change_to_pkgpart', 'int', 'NULL', '', '', '',
+ 'units_taxproductnum','int','NULL', '', '', '',
],
'primary_key' => 'pkgpart',
'unique' => [],
@@ -3265,6 +3266,10 @@ sub tables_hashref {
{ columns => [ 'taxproductnum' ],
table => 'part_pkg_taxproduct',
},
+ { columns => [ 'units_taxproductnum' ],
+ table => 'part_pkg_taxproduct',
+ references => [ 'taxproductnum' ],
+ },
{ columns => [ 'agentnum' ],
table => 'agent',
},
diff --git a/FS/FS/TaxEngine/billsoft.pm b/FS/FS/TaxEngine/billsoft.pm
index 69717a2..9147f5c 100644
--- a/FS/FS/TaxEngine/billsoft.pm
+++ b/FS/FS/TaxEngine/billsoft.pm
@@ -188,8 +188,7 @@ sub create_batch {
# cache some things
my (%cust_pkg, %part_pkg, %cust_location, %classname);
# keys are transaction codes (the first part of the taxproduct string)
- # and then locationnums; for per-location taxes
- my %sales;
+ my %all_tcodes;
my @options = $self->conf->config('billsoft-taxconfig');
@@ -239,8 +238,7 @@ sub create_batch {
my $taxproduct = $self->part_pkg_taxproduct($part_pkg, $classnum)
or next;
- my $tcode = substr($taxproduct, 0, 6);
- my $scode = substr($taxproduct, 6, 6);
+ my ($tcode, $scode) = split(':', $taxproduct);
# For CDRs, use the call termination site rather than setting
# Termination fields to the service address.
@@ -259,9 +257,6 @@ sub create_batch {
} # while $cdr = $cdr_search->fetch
- my $recur_tcode;
- # now write lines for the non-CDR portion of the charges
-
my $locationnum = $cust_pkg->locationnum;
# use termination address for the service location
@@ -284,13 +279,10 @@ sub create_batch {
my $taxproduct = $self->part_pkg_taxproduct($part_pkg, $_);
next unless $taxproduct;
- my $tcode = substr($taxproduct, 0, 6);
- my $scode = substr($taxproduct, 6, 6);
- $sales{$tcode} ||= 0;
- $recur_tcode = $tcode if $_ eq 'recur';
+ my ($tcode, $scode) = split(':', $taxproduct);
+ $all_tcodes{$tcode} ||= 1;
my $price = $cust_bill_pkg->get($_);
- $sales{$tcode} += $price;
$price -= $usage_total if $_ eq 'recur';
@@ -305,10 +297,9 @@ sub create_batch {
} # foreach (setup, recur)
- # S-code 21: taxes based on number of lines (E911, mostly)
- # voip_cdr and voip_inbound packages know how to report this. Not all
- # T-codes are eligible for this; only report it if the /21 taxproduct
- # exists.
+ # taxes based on number of lines (E911, mostly)
+ # mostly S-code 21 but can be others, as they want to know about
+ # Centrex trunks, PBX extensions, etc.
#
# (note: the nomenclature of "service" and "transaction" codes is
# backward from the way most people would use the terms. you'd think
@@ -318,25 +309,18 @@ sub create_batch {
# to avoid confusion.)
# XXX cache me
- # XXX this isn't precisely correct. Local exchange service on
- # high-capacity trunks, Centrex, and PBX trunks are supposed to be
- # reported as three separate implicit transactions: number of trunks,
- # of outbound channels, of extensions.
- # This is also true for VoIP PBX trunks. Come back to this.
- if ( $recur_tcode ) {
- my $lines_taxproduct = FS::part_pkg_taxproduct->count(
- 'data_vendor = \'billsoft\' and taxproduct = ?',
- sprintf('%06d%06d', $recur_tcode, 21)
- );
+ if ( my $lines_taxproduct = $part_pkg->units_taxproduct ) {
my $lines = $cust_bill_pkg->units;
-
- if ( $lines_taxproduct and $lines ) {
+ my $taxproduct = $lines_taxproduct->taxproduct;
+ my ($tcode, $scode) = split(':', $taxproduct);
+ $all_tcodes{$tcode} ||= 1;
+ if ( $lines ) {
$csv->print_hr($fh, {
%pkg_properties,
%termination,
RequestType => 'CalcTaxes',
- TransactionType => $recur_tcode,
- ServiceType => 21,
+ TransactionType => $tcode,
+ ServiceType => $scode,
Charge => 0,
Lines => $lines,
} );
@@ -345,12 +329,16 @@ sub create_batch {
} # foreach my $cust_bill_pkg
- foreach my $tcode (keys %sales) {
+ foreach my $tcode (keys %all_tcodes) {
- # S-code 43: per-invoice tax (apparently this is a thing)
+ # S-code 43: per-invoice tax
+ # XXX not exactly correct; there's "Invoice Bundle" (7:94) and
+ # "Centrex Invoice" (7:623). Local Exchange service would benefit from
+ # more high-level selection of the tax properties. (Infer from the FCC
+ # reporting options?)
my $invoice_taxproduct = FS::part_pkg_taxproduct->count(
'data_vendor = \'billsoft\' and taxproduct = ?',
- sprintf('%06d%06d', $tcode, 43)
+ $tcode . ':43'
);
if ( $invoice_taxproduct ) {
$csv->print_hr($fh, {
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index 35f178e..ae63487 100644
--- a/FS/FS/part_pkg.pm
+++ b/FS/FS/part_pkg.pm
@@ -735,6 +735,7 @@ sub check {
|| $self->ut_floatn('pay_weight')
|| $self->ut_floatn('credit_weight')
|| $self->ut_numbern('taxproductnum')
+ || $self->ut_numbern('units_taxproductnum')
|| $self->ut_foreign_keyn('classnum', 'pkg_class', 'classnum')
|| $self->ut_foreign_keyn('addon_classnum', 'pkg_class', 'classnum')
|| $self->ut_foreign_keyn('taxproductnum',
@@ -1731,6 +1732,19 @@ sub taxproduct_description {
$part_pkg_taxproduct ? $part_pkg_taxproduct->description : '';
}
+=item units_taxproduct
+
+Returns the L<FS::part_pkg_taxproduct> record used to report the taxable
+service units (usually phone lines) on this package.
+
+=cut
+
+sub units_taxproduct {
+ my $self = shift;
+ $self->units_taxproductnum
+ ? FS::part_pkg_taxproduct->by_key($self->units_taxproductnum)
+ : '';
+}
=item tax_rates DATA_PROVIDER, GEOCODE, [ CLASS ]
diff --git a/FS/FS/part_pkg_taxproduct.pm b/FS/FS/part_pkg_taxproduct.pm
index e86d028..51bc37f 100644
--- a/FS/FS/part_pkg_taxproduct.pm
+++ b/FS/FS/part_pkg_taxproduct.pm
@@ -223,7 +223,8 @@ sub batch_import {
my $imported = 0;
my $csv = Text::CSV_XS->new;
- # fields: taxproduct, description
+ my $error;
+ # for importing the "transervdesc.txt" file
while ( my $row = $csv->getline($fh) ) {
if (!defined $row) {
$dbh->rollback if $oldAutoCommit;
@@ -236,15 +237,32 @@ sub batch_import {
);
}
- my $new = FS::part_pkg_taxproduct->new({
- 'data_vendor' => 'billsoft',
- 'taxproduct' => $row->[0],
- 'description' => $row->[1],
+ # columns 0-2: irrelevant here
+ my $taxproduct = $row->[3] . ':' . $row->[5];
+ my $description = $row->[4];
+ $description =~ s/\s+$//;
+ $description .= ':' . $row->[6];
+ $description =~ s/\s+$//;
+ my $ppt = qsearchs('part_pkg_taxproduct', {
+ 'data_vendor' => 'billsoft',
+ 'taxproduct' => $taxproduct
});
- my $error = $new->insert;
+ if ( $ppt ) {
+ $ppt->set('description', $description);
+ $ppt->set('note', $row->[7]);
+ $error = $ppt->replace;
+ } else {
+ $ppt = FS::part_pkg_taxproduct->new({
+ 'data_vendor' => 'billsoft',
+ 'taxproduct' => $taxproduct,
+ 'description' => $description,
+ 'note' => $row->[7],
+ });
+ $error = $ppt->insert;
+ }
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return "error inserting part_pkg_taxproduct: $error\n";
+ return "error inserting part_pkg_taxproduct $taxproduct: $error\n";
}
$imported++;
}