summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Record.pm2
-rw-r--r--FS/FS/cust_main.pm74
-rw-r--r--FS/FS/tax_rate.pm63
3 files changed, 129 insertions, 10 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index 5a446d886..e2d0a0fc5 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -1923,7 +1923,7 @@ sub ut_agentnum_acl {
if ( $self->$field() ) {
- return "Access deined"
+ return "Access denied"
unless $curuser->agentnum($self->$field());
} else {
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index c28991fff..d49091671 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2071,6 +2071,7 @@ sub bill {
my( $total_setup, $total_recur ) = ( 0, 0 );
my %tax;
my %taxlisthash;
+ my %taxname;
my @precommit_hooks = ();
foreach my $cust_pkg (
@@ -2292,7 +2293,7 @@ sub bill {
})
if scalar(@taxclassnums);
-
+
}else{
my %taxhash = map { $_ => $self->get("$prefix$_") }
@@ -2364,20 +2365,85 @@ sub bill {
return '';
}
+ warn "having a look at the taxes we found...\n" if $DEBUG > 2;
foreach my $tax ( keys %taxlisthash ) {
my $tax_object = shift @{ $taxlisthash{$tax} };
+ warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2;
my $listref_or_error = $tax_object->taxline( @{ $taxlisthash{$tax} } );
unless (ref($listref_or_error)) {
$dbh->rollback if $oldAutoCommit;
return $listref_or_error;
}
+ unshift @{ $taxlisthash{$tax} }, $tax_object;
- $tax{ $listref_or_error->[0] } += $listref_or_error->[1];
+ warn "adding ". $listref_or_error->[1].
+ " as ". $listref_or_error->[0]. "\n"
+ if $DEBUG > 2;
+ $tax{ $tax_object->taxname } += $listref_or_error->[1];
+ if ( $taxname{ $listref_or_error->[0] } ) {
+ push @{ $taxname{ $listref_or_error->[0] } }, $tax_object->taxname;
+ }else{
+ $taxname{ $listref_or_error->[0] } = [ $tax_object->taxname ];
+ }
}
- foreach my $taxname ( grep { $tax{$_} > 0 } keys %tax ) {
- my $tax = sprintf('%.2f', $tax{$taxname} );
+ #some taxes are taxed
+ my %totlisthash;
+
+ warn "finding taxed taxes...\n" if $DEBUG > 2;
+ foreach my $tax ( keys %taxlisthash ) {
+ my $tax_object = shift @{ $taxlisthash{$tax} };
+ warn "found possible taxed tax ". $tax_object->taxname. " we call $tax\n"
+ if $DEBUG > 2;
+ next unless $tax_object->can('tax_on_tax');
+
+ foreach my $tot ( $tax_object->tax_on_tax( $self ) ) {
+ my $totname = ref( $tot ). ' '. $tot->taxnum;
+
+ warn "checking $totname which we call ". $tot->taxname. " as applicable\n"
+ if $DEBUG > 2;
+ next unless exists( $taxlisthash{ $totname } ); # only increase
+ # existing taxes
+ warn "adding $totname to taxed taxes\n" if $DEBUG > 2;
+ if ( exists( $totlisthash{ $totname } ) ) {
+ push @{ $totlisthash{ $totname } }, $tax{ $tax_object->taxname };
+ }else{
+ $totlisthash{ $totname } = [ $tot, $tax{ $tax_object->taxname } ];
+ }
+ }
+ }
+
+ warn "having a look at taxed taxes...\n" if $DEBUG > 2;
+ foreach my $tax ( keys %totlisthash ) {
+ my $tax_object = shift @{ $totlisthash{$tax} };
+ warn "found previously found taxed tax ". $tax_object->taxname. "\n"
+ if $DEBUG > 2;
+ my $listref_or_error = $tax_object->taxline( @{ $totlisthash{$tax} } );
+ unless (ref($listref_or_error)) {
+ $dbh->rollback if $oldAutoCommit;
+ return $listref_or_error;
+ }
+
+ warn "adding taxed tax amount ". $listref_or_error->[1].
+ " as ". $tax_object->taxname. "\n"
+ if $DEBUG;
+ $tax{ $tax_object->taxname } += $listref_or_error->[1];
+ }
+
+ #consolidate and create tax line items
+ warn "consolidating and generating...\n" if $DEBUG > 2;
+ foreach my $taxname ( keys %taxname ) {
+ my $tax = 0;
+ my %seen = ();
+ warn "adding $taxname\n" if $DEBUG > 1;
+ foreach my $taxitem ( @{ $taxname{$taxname} } ) {
+ $tax += $tax{$taxitem} unless $seen{$taxitem};
+ warn "adding $tax{$taxitem}\n" if $DEBUG > 1;
+ }
+ next unless $tax;
+
+ $tax = sprintf('%.2f', $tax );
$total_setup = sprintf('%.2f', $total_setup+$tax );
push @cust_bill_pkg, new FS::cust_bill_pkg {
diff --git a/FS/FS/tax_rate.pm b/FS/FS/tax_rate.pm
index 18f2e110e..81b63abd7 100644
--- a/FS/FS/tax_rate.pm
+++ b/FS/FS/tax_rate.pm
@@ -7,11 +7,12 @@ use vars qw( @ISA $DEBUG $me
use Date::Parse;
use Storable qw( thaw );
use MIME::Base64;
-use FS::Record qw( qsearchs dbh );
+use FS::Record qw( qsearch qsearchs dbh );
use FS::tax_class;
use FS::cust_bill_pkg;
use FS::cust_tax_location;
use FS::part_pkg_taxrate;
+use FS::cust_main;
@ISA = qw( FS::Record );
@@ -338,16 +339,18 @@ sub passtype_name {
$tax_passtypes{$self->passtype};
}
-=item taxline CUST_BILL_PKG, ...
+=item taxline CUST_BILL_PKG|AMOUNT, ...
Returns a listref of a name and an amount of tax calculated for the list
-of packages. If an error occurs, a message is returned as a scalar.
+of packages/amounts. If an error occurs, a message is returned as a scalar.
=cut
sub taxline {
my $self = shift;
- my @cust_bill_pkg = @_;
+
+ my $taxable_charged = 0;
+ my @cust_bill_pkg = grep { $taxable_charged += $_ unless ref; ref; } @_;
warn "calculating taxes for ". $self->taxnum. " on ".
join (",", map { $_->pkgnum } @cust_bill_pkg)
@@ -380,7 +383,6 @@ sub taxline {
if ($self->passtype == 2);
my $amount = 0;
- my $taxable_charged = 0;
unless ($self->setuptax =~ /^Y$/i) {
$taxable_charged += $_->setup foreach @cust_bill_pkg;
}
@@ -419,6 +421,57 @@ sub taxline {
}
+=item tax_on_tax CUST_MAIN
+
+Returns a list of taxes which are candidates for taxing taxes for the
+given customer (see L<FS::cust_main>)
+
+=cut
+
+sub tax_on_tax {
+ my $self = shift;
+ my $cust_main = shift;
+
+ warn "looking up taxes on tax ". $self->taxnum. " for customer ".
+ $cust_main->custnum
+ if $DEBUG;
+
+ my $geocode = $cust_main->geocode($self->data_vendor);
+
+ # CCH oddness in m2m
+ my $dbh = dbh;
+ my $extra_sql = ' AND ('.
+ join(' OR ', map{ 'geocode = '. $dbh->quote(substr($geocode, 0, $_)) }
+ qw(10 5 2)
+ ).
+ ')';
+
+ my $order_by = 'ORDER BY taxclassnum, length(geocode) desc';
+ my $select = 'DISTINCT ON(taxclassnum) *';
+
+ # should qsearch preface columns with the table to facilitate joins?
+ my @taxclassnums = map { $_->taxclassnum }
+ qsearch( { 'table' => 'part_pkg_taxrate',
+ 'select' => $select,
+ 'hashref' => { 'data_vendor' => $self->data_vendor,
+ 'taxclassnumtaxed' => $self->taxclassnum,
+ },
+ 'extra_sql' => $extra_sql,
+ 'order_by' => $order_by,
+ } );
+
+ return () unless @taxclassnums;
+
+ $extra_sql =
+ "AND (". join(' OR ', map { "taxclassnum = $_" } @taxclassnums ). ")";
+
+ qsearch({ 'table' => 'tax_rate',
+ 'hashref' => { 'geocode' => $geocode, },
+ 'extra_sql' => $extra_sql,
+ })
+
+}
+
=back
=head1 SUBROUTINES