when crediting unused time on packages, also credit any tax that was charged, #42729
authorMark Wells <mark@freeside.biz>
Tue, 20 Sep 2016 18:39:37 +0000 (11:39 -0700)
committerMark Wells <mark@freeside.biz>
Tue, 20 Sep 2016 20:00:29 +0000 (13:00 -0700)
FS/FS/part_pkg/flat.pm
FS/t/suite/09-sales_tax_credit_change.t [new file with mode: 0755]

index 84599ea..f23717b 100644 (file)
@@ -235,6 +235,8 @@ sub calc_remain {
     $time = time;
   }
 
+  my $sources = $options{'cust_credit_source_bill_pkg'};
+
   my $next_bill = $cust_pkg->getfield('bill') || 0;
 
   return 0 if    ! $self->base_recur($cust_pkg, \$time)
@@ -270,17 +272,30 @@ sub calc_remain {
       $amount = $amount * ($edate - $time) / ($edate - $cust_bill_pkg->sdate);
     }
 
+    # calculate tax adjustment. we're not doing full credit_lineitems here
+    # (e.g. not applying the credit to the past billing of this package)
+    # so just include the adjustment in the source record with the rest
+    # of the credit
+    my %tax_adjust = FS::cust_credit->calculate_tax_adjustment(
+      'custnum'     => $cust_pkg->custnum,
+      'billpkgnums' => [ $cust_bill_pkg->billpkgnum ],
+      'setuprecurs' => [ 'recur' ],
+      'amounts'     => [ $amount ],
+    );
+    $amount += $tax_adjust{taxtotal};
+
+    $amount = sprintf('%.2f', $amount); # ensure that amounts add up right
     $credit += $amount;
 
-    push @{ $options{'cust_credit_source_bill_pkg'} },
-      new FS::cust_credit_source_bill_pkg {
-        'billpkgnum' => $cust_bill_pkg->billpkgnum,
-        'amount'     => sprintf('%.2f', $amount),
-        'currency'   => $cust_bill_pkg->cust_bill->currency,
-      }
-        if $options{'cust_credit_source_bill_pkg'};
-
-  } 
+    if ( $sources ) {
+      push @$sources,
+        FS::cust_credit_source_bill_pkg->new( {
+          'billpkgnum' => $cust_bill_pkg->billpkgnum,
+          'amount'     => $amount,
+          'currency'   => $cust_bill_pkg->cust_bill->currency,
+        } );
+    }
+  } # foreach $cust_bill_pkg
 
   sprintf('%.2f', $credit);
 
diff --git a/FS/t/suite/09-sales_tax_credit_change.t b/FS/t/suite/09-sales_tax_credit_change.t
new file mode 100755 (executable)
index 0000000..5af7ae0
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+=head2 DESCRIPTION
+
+Tests crediting a package for unused time when it has sales tax. See
+RT#42729.
+
+The package will be billed for $30.00 with 10% tax, then credited for 1/3
+of the billing period.
+
+Correct: The credit amount will be $11.00.
+
+=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::part_pkg;
+use FS::Conf;
+my $FS= FS::Test->new;
+
+# Create a package def
+my $error;
+my $part_pkg = FS::part_pkg->new({
+  pkg     => 'Tax credit test',
+  plan    => 'flat',
+  freq    => '1',
+  agentnum => 1,
+});
+my %options = (
+  'setup_fee' => 0,
+  'recur_fee' => 30.00,
+  'recur_temporality' => 'upcoming',
+  'unused_credit_cancel' => '1',
+);
+$error = $part_pkg->insert(options => \%options);
+BAIL_OUT("can't create package def: $error") if $error;
+
+# Create the customer and order a package
+my $cust = $FS->new_customer('Credit unused with taxes');
+$cust->bill_location->state('AK');
+$error = $cust->insert;
+BAIL_OUT("can't create test customer: $error") if $error;
+
+my $pkg = FS::cust_pkg->new({ pkgpart => $part_pkg->pkgpart });
+$error = $cust->order_pkg({ cust_pkg => $pkg });
+BAIL_OUT("can't create test charges: $error") if $error;
+
+# Create tax def
+my $cust_main_county = FS::cust_main_county->new({
+  'country'       => 'US',
+  'state'         => 'AK',
+  'exempt_amount' => 0.00,
+  'taxname'       => 'Test tax',
+  'tax'           => '10',
+});
+$error = $cust_main_county->insert;
+BAIL_OUT("can't create tax definitions: $error") if $error;
+
+# Bill the customer on Apr 1
+# (April because it's 30 days, and also doesn't have DST)
+set_fixed_time(str2time('2016-04-01 00: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 amount
+my ($tax_item) = grep { $_->itemdesc eq $cust_main_county->taxname }
+                $cust_bill->cust_bill_pkg;
+ok ( $tax_item && $tax_item->setup == 3.00, "Tax charged = 3.00" );
+
+# sync
+$pkg = $pkg->replace_old;
+
+# Now cancel with 1/3 of the period left
+set_fixed_time(str2time('2016-04-21 00:00'));
+$error = $pkg->cancel();
+BAIL_OUT("can't cancel package: $error") if $error;
+
+# and find the credit
+my ($credit) = $cust->cust_credit
+  or BAIL_OUT("no credit was created");
+ok ( $credit->amount == 11.00, "Credited 1/3 of package charge with tax" )
+  or diag("is ". $credit->amount );