[freeside-commits] branch master updated. b5f68a61dad0e3d00bab85716dc97bc186f55e48

Mark Wells mark at 420.am
Thu Jun 16 18:56:42 PDT 2016


The branch, master has been updated
       via  b5f68a61dad0e3d00bab85716dc97bc186f55e48 (commit)
      from  5a6c2cf5ca5f1a80cad9310ec239a64dc8a280c1 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit b5f68a61dad0e3d00bab85716dc97bc186f55e48
Author: Mark Wells <mark at freeside.biz>
Date:   Thu Jun 16 18:17:20 2016 -0700

    fix sales tax rounding in some edge cases, #42263, from #39487

diff --git a/FS/FS/TaxEngine/internal.pm b/FS/FS/TaxEngine/internal.pm
index a9b32d1..db7010c 100644
--- a/FS/FS/TaxEngine/internal.pm
+++ b/FS/FS/TaxEngine/internal.pm
@@ -66,7 +66,7 @@ sub taxline {
   my $taxnum = $tax_object->taxnum;
   my $exemptions = $self->{exemptions}->{$taxnum} ||= [];
   
-  my $taxable_cents = 0;
+  my $taxable_total = 0;
   my $tax_cents = 0;
 
   my $round_per_line_item = $conf->exists('tax-round_per_line_item');
@@ -302,15 +302,17 @@ sub taxline {
     });
     push @tax_links, $location;
 
-    $taxable_cents += $taxable_charged;
+    $taxable_total += $taxable_charged;
     $tax_cents += $this_tax_cents;
   } #foreach $cust_bill_pkg
 
-  # calculate tax and rounding error for the whole group
-  my $extra_cents = sprintf('%.2f', $taxable_cents * $tax_object->tax / 100)
-                            * 100 - $tax_cents;
-  # make sure we have an integer
-  $extra_cents = sprintf('%.0f', $extra_cents);
+  # calculate tax and rounding error for the whole group: total taxable
+  # amount times tax rate (as cents per dollar), minus the tax already
+  # charged
+  # and force 0.5 to round up
+  my $extra_cents = sprintf('%.0f',
+    ($taxable_total * $tax_object->tax) - $tax_cents + 0.00000001
+  );
 
   # if we're rounding per item, then ignore that and don't distribute any
   # extra cents.
diff --git a/FS/t/suite/08-sales_tax.t b/FS/t/suite/08-sales_tax.t
new file mode 100755
index 0000000..bf1ae48
--- /dev/null
+++ b/FS/t/suite/08-sales_tax.t
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+
+=head2 DESCRIPTION
+
+Tests basic sales tax calculations, including consolidation and rounding.
+The invoice will have two charges that add up to $50 and two taxes:
+- Tax 1, 8.25%, for $4.125 in tax, which will round up.
+- Tax 2, 8.245%, for $4.1225 in tax, which will round down.
+
+Correct: The invoice will have one line item for each of those taxes, with
+the correct amount.
+
+=cut
+
+use strict;
+use Test::More tests => 2;
+use FS::Test;
+use Date::Parse 'str2time';
+use Date::Format 'time2str';
+use Test::MockTime qw(set_fixed_time);
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::Conf;
+my $FS= FS::Test->new;
+
+# test configuration
+my @taxes = (
+  [ 'Tax 1', 8.250, 4.13 ],
+  [ 'Tax 2', 8.245, 4.12 ],
+);
+ 
+# Create the customer and charge them
+my $cust = $FS->new_customer('Basic taxes');
+$cust->bill_location->state('AZ'); # move it away from the default of CA
+my $error;
+$error = $cust->insert;
+BAIL_OUT("can't create test customer: $error") if $error;
+$error = $cust->charge( {
+  amount    => 25.00,
+  pkg       => 'Test charge 1',
+} ) || 
+$cust->charge({
+  amount    => 25.00,
+  pkg       => 'Test charge 2',
+});
+BAIL_OUT("can't create test charges: $error") if $error;
+
+# Create tax defs
+foreach my $tax (@taxes) {
+  my $cust_main_county = FS::cust_main_county->new({
+    'country'       => 'US',
+    'state'         => 'AZ',
+    'exempt_amount' => 0.00,
+    'taxname'       => $tax->[0],
+    'tax'           => $tax->[1],
+  });
+  $error = $cust_main_county->insert;
+  BAIL_OUT("can't create tax definitions: $error") if $error;
+}
+
+# Bill the customer
+set_fixed_time(str2time('2016-03-10 08:00'));
+my @return;
+$error = $cust->bill( return_bill => \@return );
+BAIL_OUT("can't bill charges: $error") if $error;
+my $cust_bill = $return[0] or BAIL_OUT("no invoice generated");
+# Check amounts
+diag("Tax on 25.00 + 25.00");
+foreach my $cust_bill_pkg ($cust_bill->cust_bill_pkg) {
+  next if $cust_bill_pkg->pkgnum;
+  my ($tax) = grep { $_->[0] eq $cust_bill_pkg->itemdesc } @taxes;
+  if ( $tax ) {
+    ok ( $cust_bill_pkg->setup eq $tax->[2], "Tax at rate $tax->[1]% = $tax->[2]")
+      or diag("is ". $cust_bill_pkg->setup);
+  }
+}

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/TaxEngine/internal.pm |   16 +++++----
 FS/t/suite/08-sales_tax.t   |   76 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 7 deletions(-)
 create mode 100755 FS/t/suite/08-sales_tax.t




More information about the freeside-commits mailing list