[freeside-commits] branch master updated. faa92dd0fd0b875886378de7c91c59a4049f1168

Mark Wells mark at 420.am
Tue Mar 11 10:49:35 PDT 2014


The branch, master has been updated
       via  faa92dd0fd0b875886378de7c91c59a4049f1168 (commit)
      from  edd6f28f11fb048441b30c9d156e48cf4357774b (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 faa92dd0fd0b875886378de7c91c59a4049f1168
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Mar 11 10:47:17 2014 -0700

    fix tax calculation on bundled packages, fallout from #25899

diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 066ddf1..ef9c01a 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -982,8 +982,8 @@ charge.  If called on a tax line, returns nothing.
 
 sub part_X {
   my $self = shift;
-  if ( $self->override_pkgpart ) {
-    return FS::part_pkg->by_key($self->override_pkgpart);
+  if ( $self->pkgpart_override ) {
+    return FS::part_pkg->by_key($self->pkgpart_override);
   } elsif ( $self->pkgnum ) {
     return $self->cust_pkg->part_pkg;
   } elsif ( $self->feepart ) {
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 58bd475..156ba5f 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -22,6 +22,7 @@ use FS::cust_event;
 use FS::agent;
 use FS::sales;
 use FS::cust_credit_void;
+use FS::cust_bill_pkg;
 use FS::upgrade_journal;
 
 $me = '[ FS::cust_credit ]';
@@ -689,7 +690,6 @@ Example:
 =cut
 
 #maybe i should just be an insert with extra args instead of a class method
-use FS::cust_bill_pkg;
 sub credit_lineitems {
   my( $class, %arg ) = @_;
   my $curuser = $FS::CurrentUser::CurrentUser;
@@ -815,9 +815,10 @@ sub credit_lineitems {
 
     # recalculate taxes with new amounts
     $taxlisthash{$invnum} ||= {};
-    my $part_pkg = $cust_bill_pkg->part_pkg
-      if $cust_bill_pkg->pkgpart_override;
-    $cust_main->_handle_taxes( $taxlisthash{$invnum}, $cust_bill_pkg );
+    if ( $cust_bill_pkg->pkgnum or $cust_bill_pkg->feepart ) {
+      $cust_main->_handle_taxes( $taxlisthash{$invnum}, $cust_bill_pkg );
+    } # otherwise the item itself is a tax, and assume the caller knows
+      # what they're doing
   }
 
   ###
diff --git a/bin/fix-missing-taxes b/bin/fix-missing-taxes
new file mode 100644
index 0000000..62684ce
--- /dev/null
+++ b/bin/fix-missing-taxes
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+
+=head1 fix-missing-taxes
+
+Usage:
+  fix-missing-taxes <user> <start date>
+
+This script fixes CCH taxes that were calculated incorrectly due to a bug 
+in bundled package behavior in March 2014.  For all invoices since the start
+date, it recalculates taxes on all the non-tax items, generates credits for
+taxes that were originally overcharged, and creates new invoices for taxes
+that were undercharged.
+
+=cut
+
+use FS::UID qw(adminsuidsetup dbh);
+use FS::cust_bill;
+use FS::Record qw(qsearch);
+use List::Util 'sum';
+use DateTime::Format::Natural;
+
+use strict;
+
+my $usage = "usage: fix-missing-taxes <user> <start date>\n" ;
+my $user = shift or die $usage;
+adminsuidsetup($user);
+
+$FS::UID::AutoCommit = 0;
+
+my $parser = DateTime::Format::Natural->new;
+my $dt = $parser->parse_datetime(shift);
+die $usage unless $parser->success;
+
+my $date_filter = { _date => { op => '>=', value => $dt->epoch } };
+my @bills = qsearch('cust_bill', $date_filter);
+
+warn "Examining ".scalar(@bills)." invoices...\n";
+
+my %new_tax_items; # custnum => [ new taxes to charge ]
+my %cust_credits; # custnum => { tax billpkgnum => credit amount }
+
+foreach my $cust_bill (@bills) {
+  my $cust_main = $cust_bill->cust_main;
+  my $custnum = $cust_main->custnum;
+  my %taxlisthash;
+  my %old_tax;
+  my @nontax_items;
+
+  foreach my $item ($cust_bill->cust_bill_pkg) {
+    if ( $item->pkgnum == 0 ) {
+      $old_tax{ $item->itemdesc } = $item;
+    } else {
+      $cust_main->_handle_taxes( \%taxlisthash, $item );
+      push @nontax_items, $item;
+    }
+  }
+  my $tax_lines = $cust_main->calculate_taxes(
+    \@nontax_items,
+    \%taxlisthash,
+    $cust_bill->_date
+  );
+
+  my %new_tax = map { $_->itemdesc, $_ } @$tax_lines;
+  my %all = (%old_tax, %new_tax);
+  foreach my $taxname (keys(%all)) {
+    my $delta = sprintf('%.2f',
+                  ($new_tax{$taxname} ? $new_tax{$taxname}->setup : 0) -
+                  ($old_tax{$taxname} ? $old_tax{$taxname}->setup : 0)
+                );
+    if ( $delta >= 0.01 ) {
+      # create a tax adjustment
+      $new_tax_items{$custnum} ||= [];
+      my $item = $new_tax{$taxname};
+      foreach (@{ $item->cust_bill_pkg_tax_rate_location }) {
+        $_->set('amount',
+          sprintf('%.2f', $_->get('amount') * $delta / $item->get('setup'))
+        );
+      }
+      $item->set('setup', $delta);
+      push @{ $new_tax_items{$custnum} }, $new_tax{$taxname};
+    } elsif ( $delta <= -0.01 ) {
+      my $old_tax_item = $old_tax{$taxname};
+      $cust_credits{$custnum} ||= {};
+      $cust_credits{$custnum}{ $old_tax_item->billpkgnum } = -1 * $delta;
+    }
+  }
+}
+
+my $num_bills = 0;
+my $amt_billed = 0;
+# create new invoices for those that need them
+foreach my $custnum (keys %new_tax_items) {
+  my $cust_main = FS::cust_main->by_key($custnum);
+  my @cust_bill = $cust_main->cust_bill;
+  my $balance = $cust_main->balance;
+  my $previous_bill = $cust_bill[-1] if @cust_bill;
+  my $previous_balance = 0;
+  if ( $previous_bill ) {
+    $previous_balance = $previous_bill->billing_balance
+                      + $previous_bill->charged;
+  }
+
+  my $lines = $new_tax_items{$custnum};
+  my $total = sum( map { $_->setup } @$lines);
+  my $new_bill = FS::cust_bill->new({
+      'custnum'           => $custnum,
+      '_date'             => $^T,
+      'charged'           => sprintf('%.2f', $total),
+      'billing_balance'   => $balance,
+      'previous_balance'  => $previous_balance,
+      'cust_bill_pkg'     => $lines,
+  });
+  my $error = $new_bill->insert;
+  die "error billing cust#$custnum\n" if $error;
+  $num_bills++;
+  $amt_billed += $total;
+}
+print "Created $num_bills bills for a total of \$$amt_billed.\n";
+
+my $credit_reason = FS::reason->new_or_existing( 
+  reason  => 'Sales tax correction',
+  class   => 'R',
+  type    => 'Credit',
+);
+
+my $num_credits = 0;
+my $amt_credited = 0;
+# create credits for those that need them
+foreach my $custnum (keys %cust_credits) {
+  my $cust_main = FS::cust_main->by_key($custnum);
+  my $lines = $cust_credits{$custnum};
+  my @billpkgnums = keys %$lines;
+  my @amounts = values %$lines;
+  my $total = sprintf('%.2f', sum(@amounts));
+  next if $total < 0.01;
+  my $error = FS::cust_credit->credit_lineitems(
+    'custnum'     => $custnum,
+    'billpkgnums' => \@billpkgnums,
+    'setuprecurs' => [ map {'setup'} @billpkgnums ],
+    'amounts'     => \@amounts,,
+    'apply'       => 1,
+    'amount'      => $total,
+    'reasonnum'   => $credit_reason->reasonnum,
+  );
+  die "error crediting cust#$custnum\n" if $error;
+  $num_credits++;
+  $amt_credited += $total;
+}
+print "Created $num_credits credits for a total of \$$amt_credited.\n";
+
+dbh->commit;

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

Summary of changes:
 FS/FS/cust_bill_pkg.pm |    4 +-
 FS/FS/cust_credit.pm   |    9 ++-
 bin/fix-missing-taxes  |  151 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 158 insertions(+), 6 deletions(-)
 create mode 100644 bin/fix-missing-taxes




More information about the freeside-commits mailing list