texas tax!
authorivan <ivan>
Thu, 9 May 2002 12:38:40 +0000 (12:38 +0000)
committerivan <ivan>
Thu, 9 May 2002 12:38:40 +0000 (12:38 +0000)
16 files changed:
FS/FS/Conf.pm
FS/FS/cust_main.pm
FS/FS/cust_main_county.pm
FS/FS/part_pkg.pm
FS/t/cust_tax_exempt.t [new file with mode: 0644]
htetc/global.asa
htetc/handler.pl
httemplate/browse/agent.cgi
httemplate/browse/cust_main_county.cgi
httemplate/edit/agent.cgi
httemplate/edit/cust_main.cgi
httemplate/edit/cust_main_county-expand.cgi
httemplate/edit/cust_main_county.cgi
httemplate/edit/part_pkg.cgi
httemplate/edit/process/cust_main_county-expand.cgi
httemplate/edit/process/cust_main_county.cgi

index 6e45ec0..1264617 100644 (file)
@@ -930,6 +930,13 @@ httemplate/docs/config.html
     'type'        => 'checkbox',
   },
 
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'enable_taxclasses',
+    'section'     => 'billing',
+    'description' => 'Enable per-package tax classes',
+    'type'        => 'checkbox',
+  },
+
 );
 
 1;
 );
 
 1;
index 4316988..0faa60c 100644 (file)
@@ -26,6 +26,7 @@ use FS::queue;
 use FS::part_pkg;
 use FS::part_bill_event;
 use FS::cust_bill_event;
 use FS::part_pkg;
 use FS::part_bill_event;
 use FS::cust_bill_event;
+use FS::cust_tax_exempt;
 use FS::Msgcat qw(gettext);
 
 @ISA = qw( FS::Record );
 use FS::Msgcat qw(gettext);
 
 @ISA = qw( FS::Record );
@@ -874,8 +875,11 @@ sub bill {
   # & generate invoice database.
  
   my( $total_setup, $total_recur ) = ( 0, 0 );
   # & generate invoice database.
  
   my( $total_setup, $total_recur ) = ( 0, 0 );
-  my( $taxable_setup, $taxable_recur ) = ( 0, 0 );
+  #my( $taxable_setup, $taxable_recur ) = ( 0, 0 );
   my @cust_bill_pkg = ();
   my @cust_bill_pkg = ();
+  my $tax = 0;##
+  #my $taxable_charged = 0;##
+  #my $charged = 0;##
 
   foreach my $cust_pkg (
     qsearch('cust_pkg', { 'custnum' => $self->custnum } )
 
   foreach my $cust_pkg (
     qsearch('cust_pkg', { 'custnum' => $self->custnum } )
@@ -888,7 +892,7 @@ sub bill {
     $cust_pkg->setfield('bill', '')
       unless defined($cust_pkg->bill);
  
     $cust_pkg->setfield('bill', '')
       unless defined($cust_pkg->bill);
  
-    my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $cust_pkg->pkgpart } );
+    my $part_pkg = $cust_pkg->part_pkg;
 
     #so we don't modify cust_pkg record unnecessarily
     my $cust_pkg_mod_flag = 0;
 
     #so we don't modify cust_pkg record unnecessarily
     my $cust_pkg_mod_flag = 0;
@@ -958,7 +962,7 @@ sub bill {
       # here
       $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
 
       # here
       $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
 
-      $mon += $part_pkg->getfield('freq');
+      $mon += $part_pkg->freq;
       until ( $mon < 12 ) { $mon -= 12; $year++; }
       $cust_pkg->setfield('bill',
         timelocal($sec,$min,$hour,$mday,$mon,$year));
       until ( $mon < 12 ) { $mon -= 12; $year++; }
       $cust_pkg->setfield('bill',
         timelocal($sec,$min,$hour,$mday,$mon,$year));
@@ -969,6 +973,7 @@ sub bill {
     warn "\$recur is undefined" unless defined($recur);
     warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill);
 
     warn "\$recur is undefined" unless defined($recur);
     warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill);
 
+    my $taxable_charged = 0;
     if ( $cust_pkg_mod_flag ) {
       $error=$cust_pkg->replace($old_cust_pkg);
       if ( $error ) { #just in case
     if ( $cust_pkg_mod_flag ) {
       $error=$cust_pkg->replace($old_cust_pkg);
       if ( $error ) { #just in case
@@ -996,51 +1001,126 @@ sub bill {
         push @cust_bill_pkg, $cust_bill_pkg;
         $total_setup += $setup;
         $total_recur += $recur;
         push @cust_bill_pkg, $cust_bill_pkg;
         $total_setup += $setup;
         $total_recur += $recur;
-        $taxable_setup += $setup
-          unless $part_pkg->dbdef_table->column('setuptax')
-                 && $part_pkg->setuptax =~ /^Y$/i;
-        $taxable_recur += $recur
-          unless $part_pkg->dbdef_table->column('recurtax')
-                 && $part_pkg->recurtax =~ /^Y$/i;
-      }
-    }
-
-  }
+        $taxable_charged += $setup
+          unless $part_pkg->setuptax =~ /^Y$/i;
+        $taxable_charged += $recur
+          unless $part_pkg->recurtax =~ /^Y$/i;
+          
+        unless ( $self->tax =~ /Y/i
+                 || $self->payby eq 'COMP'
+                 || $taxable_charged == 0 ) {
+
+          my $cust_main_county =
+            qsearchs('cust_main_county',{
+              'state'    => $self->state,
+              'county'   => $self->county,
+              'country'  => $self->country,
+              'taxclass' => $part_pkg->taxclass,
+            } )
+            or qsearchs('cust_main_county',{
+              'state'    => $self->state,
+              'county'   => $self->county,
+              'country'  => $self->country,
+              'taxclass' => '',
+            } )
+            or do {
+              $dbh->rollback if $oldAutoCommit;
+              return
+                "fatal: can't find tax rate for state/county/country/taxclass ".
+                join('/', map $self->$_(), qw(state county country taxclass) ).
+                "\n";
+            };
+
+          if ( $cust_main_county->exempt_amount ) {
+            my ($mon,$year) = (localtime($sdate) )[4,5];
+            $mon++;
+            my $freq = $part_pkg->freq || 1;
+            my $taxable_per_month = sprintf("%.2f", $taxable_charged / $freq );
+            foreach my $which_month ( 1 .. $freq ) {
+              my %hash = (
+                'custnum' => $self->custnum,
+                'taxnum'  => $cust_main_county->taxnum,
+                'year'    => 1900+$year,
+                'month'   => $mon++,
+              );
+              #until ( $mon < 12 ) { $mon -= 12; $year++; }
+              until ( $mon < 13 ) { $mon -= 12; $year++; }
+              my $cust_tax_exempt =
+                qsearchs('cust_tax_exempt', \%hash)
+                || new FS::cust_tax_exempt( { %hash, 'amount' => 0 } );
+              my $remaining_exemption = sprintf("%.2f",
+                $cust_main_county->exempt_amount - $cust_tax_exempt->amount );
+              if ( $remaining_exemption > 0 ) {
+                my $addl = $remaining_exemption > $taxable_per_month
+                  ? $taxable_per_month
+                  : $remaining_exemption;
+                $taxable_charged -= $addl;
+                my $new_cust_tax_exempt = new FS::cust_tax_exempt ( {
+                  $cust_tax_exempt->hash,
+                  'amount' => sprintf("%.2f", $cust_tax_exempt->amount + $addl),
+                } );
+                $error = $new_cust_tax_exempt->exemptnum
+                  ? $new_cust_tax_exempt->replace($cust_tax_exempt)
+                  : $new_cust_tax_exempt->insert;
+                if ( $error ) {
+                  $dbh->rollback if $oldAutoCommit;
+                  return "fatal: can't update cust_tax_exempt: $error";
+                }
+
+              } # if $remaining_exemption > 0
+
+            } #foreach $which_month
+
+          } #if $cust_main_county->exempt_amount
+
+          $taxable_charged = sprintf( "%.2f", $taxable_charged);
+          $tax += $taxable_charged * $cust_main_county->tax / 100
+
+        } #unless $self->tax =~ /Y/i
+          #       || $self->payby eq 'COMP'
+          #       || $taxable_charged == 0
+
+      } #if $setup > 0 || $recur > 0
+      
+    } #if $cust_pkg_mod_flag
+
+  } #foreach my $cust_pkg
 
   my $charged = sprintf( "%.2f", $total_setup + $total_recur );
 
   my $charged = sprintf( "%.2f", $total_setup + $total_recur );
-  my $taxable_charged = sprintf( "%.2f", $taxable_setup + $taxable_recur );
+#  my $taxable_charged = sprintf( "%.2f", $taxable_setup + $taxable_recur );
 
 
-  unless ( @cust_bill_pkg ) {
+  unless ( @cust_bill_pkg ) { #don't create invoices with no line items
     $dbh->commit or die $dbh->errstr if $oldAutoCommit;
     return '';
   } 
 
     $dbh->commit or die $dbh->errstr if $oldAutoCommit;
     return '';
   } 
 
-  unless ( $self->tax =~ /Y/i
-           || $self->payby eq 'COMP'
-           || $taxable_charged == 0 ) {
-    my $cust_main_county = qsearchs('cust_main_county',{
-        'state'   => $self->state,
-        'county'  => $self->county,
-        'country' => $self->country,
-    } ) or die "fatal: can't find tax rate for state/county/country ".
-               $self->state. "/". $self->county. "/". $self->country. "\n";
-    my $tax = sprintf( "%.2f",
-      $taxable_charged * ( $cust_main_county->getfield('tax') / 100 )
-    );
-
-    if ( $tax > 0 ) {
-      $charged = sprintf( "%.2f", $charged+$tax );
-
-      my $cust_bill_pkg = new FS::cust_bill_pkg ({
-        'pkgnum' => 0,
-        'setup'  => $tax,
-        'recur'  => 0,
-        'sdate'  => '',
-        'edate'  => '',
-      });
-      push @cust_bill_pkg, $cust_bill_pkg;
-    }
+#  unless ( $self->tax =~ /Y/i
+#           || $self->payby eq 'COMP'
+#           || $taxable_charged == 0 ) {
+#    my $cust_main_county = qsearchs('cust_main_county',{
+#        'state'   => $self->state,
+#        'county'  => $self->county,
+#        'country' => $self->country,
+#    } ) or die "fatal: can't find tax rate for state/county/country ".
+#               $self->state. "/". $self->county. "/". $self->country. "\n";
+#    my $tax = sprintf( "%.2f",
+#      $taxable_charged * ( $cust_main_county->getfield('tax') / 100 )
+#    );
+
+  $tax = sprintf("%.2f", $tax);
+  if ( $tax > 0 ) {
+    $charged = sprintf( "%.2f", $charged+$tax );
+
+    my $cust_bill_pkg = new FS::cust_bill_pkg ({
+      'pkgnum' => 0,
+      'setup'  => $tax,
+      'recur'  => 0,
+      'sdate'  => '',
+      'edate'  => '',
+    });
+    push @cust_bill_pkg, $cust_bill_pkg;
   }
   }
+#  }
 
   my $cust_bill = new FS::cust_bill ( {
     'custnum' => $self->custnum,
 
   my $cust_bill = new FS::cust_bill ( {
     'custnum' => $self->custnum,
index a9a4a85..8e83b1a 100644 (file)
@@ -101,7 +101,7 @@ methods.
 sub check {
   my $self = shift;
 
 sub check {
   my $self = shift;
 
-  $self->amount(0) unless $self->amount;
+  $self->exempt_amount(0) unless $self->exempt_amount;
 
   $self->ut_numbern('taxnum')
     || $self->ut_textn('state')
 
   $self->ut_numbern('taxnum')
     || $self->ut_textn('state')
@@ -109,7 +109,7 @@ sub check {
     || $self->ut_text('country')
     || $self->ut_float('tax')
     || $self->ut_textn('taxclass') # ...
     || $self->ut_text('country')
     || $self->ut_float('tax')
     || $self->ut_textn('taxclass') # ...
-    || $self->ut_money('amount')
+    || $self->ut_money('exempt_amount')
   ;
 
 }
   ;
 
 }
index 1f31065..9c33e9a 100644 (file)
@@ -59,7 +59,7 @@ inherits from FS::Record.  The following fields are currently supported:
 
 =item recurtax - Recurring fee tax exempt flag, empty or `Y'
 
 
 =item recurtax - Recurring fee tax exempt flag, empty or `Y'
 
-=item taxclass - Texas tax class flag, empty or "none", "access", or "hosting"
+=item taxclass - Tax class flag
 
 =item plan - Price plan
 
 
 =item plan - Price plan
 
@@ -235,7 +235,7 @@ sub check {
       || $self->ut_anything('plandata')
       || $self->ut_enum('setuptax', [ '', 'Y' ] )
       || $self->ut_enum('recurtax', [ '', 'Y' ] )
       || $self->ut_anything('plandata')
       || $self->ut_enum('setuptax', [ '', 'Y' ] )
       || $self->ut_enum('recurtax', [ '', 'Y' ] )
-      || $self->ut_enum('taxclass', [ '', 'none', 'access', 'hosting' ] )
+      || $self->ut_textn('taxclass')
       || $self->ut_enum('disabled', [ '', 'Y' ] )
     ;
 }
       || $self->ut_enum('disabled', [ '', 'Y' ] )
     ;
 }
@@ -297,7 +297,7 @@ sub payby {
 
 =head1 VERSION
 
 
 =head1 VERSION
 
-$Id: part_pkg.pm,v 1.13 2002-05-04 15:00:18 ivan Exp $
+$Id: part_pkg.pm,v 1.14 2002-05-09 12:38:39 ivan Exp $
 
 =head1 BUGS
 
 
 =head1 BUGS
 
diff --git a/FS/t/cust_tax_exempt.t b/FS/t/cust_tax_exempt.t
new file mode 100644 (file)
index 0000000..8af13e3
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_tax_exempt;
+$loaded=1;
+print "ok 1\n";
index 62f4544..d04a5ed 100644 (file)
@@ -9,7 +9,7 @@ use HTML::Entities;
 use IO::Handle;
 use IO::File;
 use String::Approx qw(amatch);
 use IO::Handle;
 use IO::File;
 use String::Approx qw(amatch);
-use HTML::Widgets::SelectLayers;
+use HTML::Widgets::SelectLayers 0.02;
 use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
 use FS::Record qw(qsearch qsearchs fields dbdef);
 use FS::Conf;
 use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
 use FS::Record qw(qsearch qsearchs fields dbdef);
 use FS::Conf;
index 056efa8..49bcbc0 100644 (file)
@@ -66,7 +66,7 @@ sub handler
       use IO::Handle;
       use IO::File;
       use String::Approx qw(amatch);
       use IO::Handle;
       use IO::File;
       use String::Approx qw(amatch);
-      use HTML::Widgets::SelectLayers;
+      use HTML::Widgets::SelectLayers 0.02;
       use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
       use FS::Record qw(qsearch qsearchs fields dbdef);
       use FS::Conf;
       use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
       use FS::Record qw(qsearch qsearchs fields dbdef);
       use FS::Conf;
index c74ef2b..2465009 100755 (executable)
@@ -24,8 +24,8 @@ print &table(), <<END;
       <TR>
         <TH COLSPAN=2>Agent</TH>
         <TH>Type</TH>
       <TR>
         <TH COLSPAN=2>Agent</TH>
         <TH>Type</TH>
-        <TH><FONT SIZE=-1>Freq. (unimp.)</FONT></TH>
-        <TH><FONT SIZE=-1>Prog. (unimp.)</FONT></TH>
+        <TH><FONT SIZE=-1>Freq.</FONT></TH>
+        <TH><FONT SIZE=-1>Prog.</FONT></TH>
       </TR>
 END
 #        <TH><FONT SIZE=-1>Agent #</FONT></TH>
       </TR>
 END
 #        <TH><FONT SIZE=-1>Agent #</FONT></TH>
index 5df8dfa..9916060 100755 (executable)
@@ -1,26 +1,36 @@
 <!-- mason kludge -->
 <%
 
 <!-- mason kludge -->
 <%
 
+my $conf = new FS::Conf;
+my $enable_taxclasses = $conf->exists('enable_taxclasses');
+
 print header("Tax Rate Listing", menubar(
   'Main Menu' => $p,
   'Edit tax rates' => $p. "edit/cust_main_county.cgi",
 )),<<END;
     Click on <u>expand country</u> to specify a country's tax rates by state.
     <BR>Click on <u>expand state</u> to specify a state's tax rates by county.
 print header("Tax Rate Listing", menubar(
   'Main Menu' => $p,
   'Edit tax rates' => $p. "edit/cust_main_county.cgi",
 )),<<END;
     Click on <u>expand country</u> to specify a country's tax rates by state.
     <BR>Click on <u>expand state</u> to specify a state's tax rates by county.
-    <BR><BR>
 END
 END
-print &table(), <<END;
+
+if ( $enable_taxclasses ) {
+  print '<BR>Click on <u>expand taxclasses</u> to specify tax classes';
+}
+
+print '<BR><BR>'. &table(). <<END;
       <TR>
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
         <TH>County</TH>
       <TR>
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
         <TH>County</TH>
+        <TH>Taxclass</TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
+        <TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
       </TR>
 END
 
       </TR>
 END
 
-my @regions = sort {    $a->country cmp $b->country
-                     or $a->state   cmp $b->state
-                     or $a->county  cmp $b->county
+my @regions = sort {    $a->country  cmp $b->country
+                     or $a->state    cmp $b->state
+                     or $a->county   cmp $b->county
+                     or $a->taxclass cmp $b->taxclass
                    } qsearch('cust_main_county',{});
 
 my $sup=0;
                    } qsearch('cust_main_county',{});
 
 my $sup=0;
@@ -30,7 +40,7 @@ for ( my $i=0; $i<@regions; $i++ ) {
   my $hashref = $cust_main_county->hashref;
   print <<END;
       <TR>
   my $hashref = $cust_main_county->hashref;
   print <<END;
       <TR>
-        <TD>$hashref->{country}</TD>
+        <TD BGCOLOR="#ffffff">$hashref->{country}</TD>
 END
 
   my $j;
 END
 
   my $j;
@@ -42,7 +52,8 @@ END
     for ( $j=1; $i+$j<@regions; $j++ ) {
       last if $hashref->{country} ne $regions[$i+$j]->country
            || $hashref->{state} ne $regions[$i+$j]->state
     for ( $j=1; $i+$j<@regions; $j++ ) {
       last if $hashref->{country} ne $regions[$i+$j]->country
            || $hashref->{state} ne $regions[$i+$j]->state
-           || $hashref->{tax} != $regions[$i+$j]->tax;
+           || $hashref->{tax} != $regions[$i+$j]->tax
+           || $hashref->{exempt_amount} != $regions[$i+$j]->exempt_amount;
     }
 
     my $newsup=0;
     }
 
     my $newsup=0;
@@ -60,9 +71,9 @@ END
       $j = 1;
     }
 
       $j = 1;
     }
 
-    print "<TD ROWSPAN=$j>", $hashref->{state}
-        ? $hashref->{state}
-        : qq!(ALL) <FONT SIZE=-1>!.
+    print "<TD ROWSPAN=$j", $hashref->{state}
+        ? ' BGCOLOR="#ffffff">'. $hashref->{state}
+        : qq! BGCOLOR="#cccccc">(ALL) <FONT SIZE=-1>!.
           qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
           qq!">expand country</A></FONT>!;
 
           qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
           qq!">expand country</A></FONT>!;
 
@@ -73,11 +84,11 @@ END
 
 #  $sup=$newsup;
 
 
 #  $sup=$newsup;
 
-  print "<TD>";
+  print "<TD";
   if ( $hashref->{county} ) {
   if ( $hashref->{county} ) {
-    print $hashref->{county};
+    print ' BGCOLOR="#ffffff">'. $hashref->{county};
   } else {
   } else {
-    print "(ALL)";
+    print ' BGCOLOR="#cccccc">(ALL)';
     if ( $hashref->{state} ) {
       print qq!<FONT SIZE=-1>!.
           qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
     if ( $hashref->{state} ) {
       print qq!<FONT SIZE=-1>!.
           qq!<A HREF="${p}edit/cust_main_county-expand.cgi?!. $hashref->{taxnum}.
@@ -86,10 +97,24 @@ END
   }
   print "</TD>";
 
   }
   print "</TD>";
 
-  print <<END;
-        <TD>$hashref->{tax}%</TD>
-      </TR>
-END
+  print "<TD";
+  if ( $hashref->{taxclass} ) {
+    print ' BGCOLOR="#ffffff">'. $hashref->{taxclass};
+  } else {
+    print ' BGCOLOR="#cccccc">(ALL)';
+    if ( $enable_taxclasses ) {
+      print qq!<FONT SIZE=-1>!.
+            qq!<A HREF="${p}edit/cust_main_county-expand.cgi?taxclass!.
+            $hashref->{taxnum}. qq!">expand taxclasses</A></FONT>!;
+    }
+
+  }
+  print "</TD>";
+
+  print "<TD BGCOLOR=\"#ffffff\">$hashref->{tax}%</TD>".
+        '<TD BGCOLOR="#ffffff">$'.
+          sprintf("%.2f", $hashref->{exempt_amount} || 0). '</TD>'.
+        '</TR>';
 
 }
 
 
 }
 
index 3fca343..449456c 100755 (executable)
@@ -51,12 +51,12 @@ print <<END;
 </SELECT></TD>
 </TR>
 <TR>
 </SELECT></TD>
 </TR>
 <TR>
-  <TD ALIGN="right">Frequency (unimplemented)</TD>
-  <TD><INPUT TYPE="text" NAME="freq" VALUE="$hashref->{freq}"></TD>
+  <TD ALIGN="right"><!--Frequency--></TD>
+  <TD><INPUT TYPE="hidden" NAME="freq" VALUE="$hashref->{freq}"></TD>
 </TR>
 <TR>
 </TR>
 <TR>
-  <TD ALIGN="right">Program (unimplemented)</TD>
-  <TD><INPUT TYPE="text" NAME="prog" VALUE="$hashref->{prog}"></TD>
+  <TD ALIGN="right"><!--Program--></TD>
+  <TD><INPUT TYPE="hidden" NAME="prog" VALUE="$hashref->{prog}"></TD>
 </TR>
 </TABLE>
 END
 </TR>
 </TABLE>
 END
index 33e72d8..e92abef 100755 (executable)
@@ -98,7 +98,9 @@ if ( $custnum && ! $conf->exists('editreferrals') ) {
   print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$refnum">!;
 } else {
   my(@referrals) = qsearch('part_referral',{});
   print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$refnum">!;
 } else {
   my(@referrals) = qsearch('part_referral',{});
-  if ( scalar(@referrals) == 1 ) {
+  if ( scalar(@referrals) == 0 ) {
+    die "You have not created any advertising sources.  You must create at least one advertising source before adding a customer.  Go to ". popurl(2). "browse/part_referral.cgi and create one or more advertising sources.";
+  } elsif ( scalar(@referrals) == 1 ) {
     $refnum ||= $referrals[0]->refnum;
     print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$refnum">!;
   } else {
     $refnum ||= $referrals[0]->refnum;
     print qq!<INPUT TYPE="hidden" NAME="refnum" VALUE="$refnum">!;
   } else {
index 66e8aaf..9f314a4 100755 (executable)
@@ -1,16 +1,18 @@
 <!-- mason kludge -->
 <%
 
 <!-- mason kludge -->
 <%
 
-my($taxnum, $delim, $expansion );
+my($taxnum, $delim, $expansion, $taxclass );
+my($query) = $cgi->keywords;
 if ( $cgi->param('error') ) {
   $taxnum = $cgi->param('taxnum');
   $delim = $cgi->param('delim');
   $expansion = $cgi->param('expansion');
 if ( $cgi->param('error') ) {
   $taxnum = $cgi->param('taxnum');
   $delim = $cgi->param('delim');
   $expansion = $cgi->param('expansion');
+  $taxclass = $cgi->param('taxclass');
 } else {
 } else {
-  my ($query) = $cgi->keywords;
-  $query =~ /^(\d+)$/
-    or die "Illegal taxnum!";
-  $taxnum = $1;
+  $query =~ /^(taxclass)?(\d+)$/
+    or die "Illegal taxnum (query $query)";
+  $taxclass = $1 ? 'taxclass' : '';
+  $taxnum = $2;
   $delim = 'n';
   $expansion = '';
 }
   $delim = 'n';
   $expansion = '';
 }
@@ -31,11 +33,12 @@ print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
 print <<END;
     <FORM ACTION="${p1}process/cust_main_county-expand.cgi" METHOD=POST>
       <INPUT TYPE="hidden" NAME="taxnum" VALUE="$taxnum">
 print <<END;
     <FORM ACTION="${p1}process/cust_main_county-expand.cgi" METHOD=POST>
       <INPUT TYPE="hidden" NAME="taxnum" VALUE="$taxnum">
+      <INPUT TYPE="hidden" NAME="taxclass" VALUE="$taxclass">
       Separate by
 END
 print '<INPUT TYPE="radio" NAME="delim" VALUE="n"';
 print ' CHECKED' if $delim eq 'n';
       Separate by
 END
 print '<INPUT TYPE="radio" NAME="delim" VALUE="n"';
 print ' CHECKED' if $delim eq 'n';
-print '>line (rumor has it broken on some browsers) or',
+print '>line (broken on some browsers) or',
       '<INPUT TYPE="radio" NAME="delim" VALUE="s"';
 print ' CHECKED' if $delim eq 's';
 print '>whitespace.';
       '<INPUT TYPE="radio" NAME="delim" VALUE="s"';
 print ' CHECKED' if $delim eq 's';
 print '>whitespace.';
index a117117..7ef37a4 100755 (executable)
@@ -14,8 +14,10 @@ print qq!<FORM ACTION="!, popurl(1),
       <TR>
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
       <TR>
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
-        <TH>County</TH>
+        <TH><FONT SIZE=-1>County</FONT></TH>
+        <TH><FONT SIZE=-1>Taxclass</FONT></TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
+        <TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
       </TR>
 END
 
       </TR>
 END
 
@@ -26,22 +28,29 @@ foreach my $cust_main_county ( sort {    $a->country cmp $b->country
   my($hashref)=$cust_main_county->hashref;
   print <<END;
       <TR>
   my($hashref)=$cust_main_county->hashref;
   print <<END;
       <TR>
-        <TD>$hashref->{country}</TD>
+        <TD BGCOLOR="#ffffff">$hashref->{country}</TD>
 END
 
 END
 
-  print "<TD>", $hashref->{state}
-      ? $hashref->{state}
-      : '(ALL)'
+  print "<TD", $hashref->{state}
+      ? ' BGCOLOR="#ffffff">'.$hashref->{state}
+      : ' BGCOLOR="#cccccc">(ALL)'
     , "</TD>";
 
     , "</TD>";
 
-  print "<TD>", $hashref->{county}
-      ? $hashref->{county}
-      : '(ALL)'
+  print "<TD", $hashref->{county}
+      ? ' BGCOLOR="#ffffff">'. $hashref->{county}
+      : ' BGCOLOR="#cccccc">(ALL)'
+    , "</TD>";
+
+  print "<TD", $hashref->{taxclass}
+      ? ' BGCOLOR="#ffffff">'. $hashref->{taxclass}
+      : ' BGCOLOR="#cccccc">(ALL)'
     , "</TD>";
 
   print qq!<TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
     , "</TD>";
 
   print qq!<TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
-        qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6>%</TD></TR>!;
-END
+        qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6>%</TD>!;
+  print qq!<TD>\$<INPUT TYPE="text" NAME="exempt_amount!, $hashref->{taxnum},
+        qq!" VALUE="!, $hashref->{exempt_amount}||0, qq!" SIZE=6></TD>!;
+  print '</TR>';
 
 }
 
 
 }
 
index a63fc23..e03017d 100755 (executable)
@@ -88,6 +88,23 @@ print '>';
 
 print '</TD></TR>';
 
 
 print '</TD></TR>';
 
+my $conf = new FS::Conf;
+if ( $conf->exists('enable_taxclasses') ) {
+  print '<TR><TD ALIGN="right">Tax class</TD><TD><SELECT NAME="taxclass">';
+  my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county')
+    or die dbh->errstr;
+  $sth->execute or die $sth->errstr;
+  foreach my $taxclass ( map $_->[0], @{$sth->fetchall_arrayref} ) {
+    print qq!<OPTION VALUE="$taxclass"!;
+    print ' SELECTED' if $taxclass eq $hashref->{taxclass};
+    print qq!>$taxclass</OPTION>!;
+  }
+  print '</SELECT></TD></TR>';
+} else {
+  print
+    '<INPUT TYPE="hidden" NAME="taxclass" VALUE="'. $hashref->{taxclass}. '">';
+}
+
 print '<TR><TD ALIGN="right">Disable new orders</TD><TD>';
 print '<INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"';
 print ' CHECKED' if $hashref->{disabled} eq "Y";
 print '<TR><TD ALIGN="right">Disable new orders</TD><TD>';
 print '<INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"';
 print ' CHECKED' if $hashref->{disabled} eq "Y";
@@ -344,6 +361,14 @@ my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
 
 tie my %options, 'Tie::IxHash', map { $_=>$plans{$_}->{'name'} } keys %plans;
 
 
 tie my %options, 'Tie::IxHash', map { $_=>$plans{$_}->{'name'} } keys %plans;
 
+my @form_select = ();
+if ( $conf->exists('enable_taxclasses') ) {
+  push @form_select, 'taxclass';
+} else {
+  push @fixups, 'taxclass'; #hidden
+}
+
+
 my $widget = new HTML::Widgets::SelectLayers(
   'selected_layer' => $part_pkg->plan,
   'options'        => \%options,
 my $widget = new HTML::Widgets::SelectLayers(
   'selected_layer' => $part_pkg->plan,
   'options'        => \%options,
@@ -351,6 +376,7 @@ my $widget = new HTML::Widgets::SelectLayers(
   'form_action'    => 'process/part_pkg.cgi',
   'form_text'      => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ],
   'form_checkbox'  => [ qw(setuptax recurtax disabled) ],
   'form_action'    => 'process/part_pkg.cgi',
   'form_text'      => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ],
   'form_checkbox'  => [ qw(setuptax recurtax disabled) ],
+  'form_select'    => [ @form_select ],
   'fixup_callback' => sub {
                         #my $ = @_;
                         my $html = '';
   'fixup_callback' => sub {
                         #my $ = @_;
                         my $html = '';
index 64061de..a452711 100755 (executable)
@@ -27,7 +27,9 @@ foreach ( @expansion) {
   my(%hash)=$cust_main_county->hash;
   my($new)=new FS::cust_main_county \%hash;
   $new->setfield('taxnum','');
   my(%hash)=$cust_main_county->hash;
   my($new)=new FS::cust_main_county \%hash;
   $new->setfield('taxnum','');
-  if ( ! $cust_main_county->state ) {
+  if ( $cgi->param('taxclass') ) {
+    $new->setfield('taxclass', $_);
+  } elsif ( ! $cust_main_county->state ) {
     $new->setfield('state',$_);
   } else {
     $new->setfield('county',$_);
     $new->setfield('state',$_);
   } else {
     $new->setfield('county',$_);
index 0800789..990a239 100755 (executable)
@@ -1,13 +1,16 @@
 <%
 
 <%
 
-foreach ( $cgi->param ) {
+foreach ( grep { /^tax\d+$/ } $cgi->param ) {
   /^tax(\d+)$/ or die "Illegal form $_!";
   my($taxnum)=$1;
   my($old)=qsearchs('cust_main_county',{'taxnum'=>$taxnum})
     or die "Couldn't find taxnum $taxnum!";
   /^tax(\d+)$/ or die "Illegal form $_!";
   my($taxnum)=$1;
   my($old)=qsearchs('cust_main_county',{'taxnum'=>$taxnum})
     or die "Couldn't find taxnum $taxnum!";
-  next unless $old->getfield('tax') ne $cgi->param("tax$taxnum");
-  my(%hash)=$old->hash;
-  $hash{tax}=$cgi->param("tax$taxnum");
+  my $exempt_amount = $cgi->param("exempt_amount$taxnum");
+  next unless $old->tax ne $cgi->param("tax$taxnum")
+              || $old->exempt_amount ne $exempt_amount;
+  my %hash = $old->hash;
+  $hash{tax} = $cgi->param("tax$taxnum");
+  $hash{exempt_amount} = $exempt_amount;
   my($new)=new FS::cust_main_county \%hash;
   my($error)=$new->replace($old);
   if ( $error ) {
   my($new)=new FS::cust_main_county \%hash;
   my($error)=$new->replace($old);
   if ( $error ) {