From: jeff Date: Wed, 4 Jun 2008 13:28:18 +0000 (+0000) Subject: tax on tax X-Git-Tag: root_of_webpay_support~602 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=3b35ccbf226efe00c94f3a72dd1c7ed64d926a7c tax on tax --- 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) + +=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