X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FReport%2FTax.pm;h=76012fb01615662a937987bba6771294f2641605;hb=a1c46091ebd0cc17bcbe19be266dc2efa9f2d92b;hp=d3322af87afa864e03bc9bc3e3be49e14a7637ad;hpb=48ddc2a473201e09daaff99b3dd22d56ebe8f939;p=freeside.git diff --git a/FS/FS/Report/Tax.pm b/FS/FS/Report/Tax.pm index d3322af87..76012fb01 100644 --- a/FS/FS/Report/Tax.pm +++ b/FS/FS/Report/Tax.pm @@ -2,7 +2,7 @@ package FS::Report::Tax; use strict; use vars qw($DEBUG); -use FS::Record qw(dbh qsearch qsearchs); +use FS::Record qw(dbh qsearch qsearchs group_concat_sql); use Date::Format qw( time2str ); use Data::Dumper; @@ -41,6 +41,8 @@ sub report_internal { my ($taxname, $country, %breakdown); + # purify taxname properly here, as we're going to include it in lots of + # SQL statements using single quotes only if ( $opt{taxname} =~ /^([\w\s]+)$/ ) { $taxname = $1; } else { @@ -93,6 +95,10 @@ sub report_internal { my $pkg_tax_exempt = "SELECT SUM(amount) AS exempt_charged, billpkgnum, taxnum ". "FROM cust_tax_exempt_pkg EXEMPT_WHERE GROUP BY billpkgnum, taxnum"; + # This just calculates the sum of credit applications to a line item. + my $pkg_credited = "SELECT SUM(amount) AS credited, billpkgnum ". + "FROM cust_credit_bill_pkg GROUP BY billpkgnum"; + my $where = "WHERE cust_bill._date >= $beginning AND cust_bill._date <= $ending ". "AND COALESCE(cust_main_county.taxname,'Tax') = '$taxname' ". "AND cust_main_county.country = '$country'"; @@ -107,7 +113,8 @@ sub report_internal { $select .= "NULL AS $_, "; } } - $select .= "array_to_string(array_agg(DISTINCT(cust_main_county.taxnum)), ',') AS taxnums, "; + $select .= group_concat_sql('DISTINCT(cust_main_county.taxnum)', ',') . + ' AS taxnums, '; $group =~ s/, $//; # SELECT/GROUP clauses for second-level (totals) queries @@ -118,7 +125,8 @@ sub report_internal { $select_all = "SELECT $breakdown{pkgclass} AS pkgclass, "; $group_all = "GROUP BY $breakdown{pkgclass}"; } - $select_all .= "array_to_string(array_agg(DISTINCT(cust_main_county.taxnum)), ',') AS taxnums, "; + $select_all .= group_concat_sql('DISTINCT(cust_main_county.taxnum)', ',') . + ' AS taxnums, '; my $agentnum; if ( $opt{agentnum} and $opt{agentnum} =~ /^(\d+)$/ ) { @@ -174,34 +182,88 @@ sub report_internal { $all_sql{exempt_monthly} =~ s/EXEMPT_WHERE/WHERE exempt_monthly = 'Y'/; # taxable sales + # (sale - exemptions - credits, except not negative) $sql{taxable} = "$select - SUM(cust_bill_pkg.setup + cust_bill_pkg.recur - COALESCE(exempt_charged, 0)) - FROM cust_main_county - JOIN ($pkg_tax) AS pkg_tax USING (taxnum) - JOIN cust_bill_pkg USING (billpkgnum) + SUM( + cust_bill_pkg.setup + cust_bill_pkg.recur + - COALESCE(exempt_charged, 0) + - COALESCE(credited, 0) + ) + FROM cust_bill_pkg + LEFT JOIN ($pkg_tax) AS pkg_tax + ON (cust_bill_pkg.billpkgnum = pkg_tax.billpkgnum) LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt - ON (pkg_tax_exempt.billpkgnum = cust_bill_pkg.billpkgnum - AND pkg_tax_exempt.taxnum = cust_main_county.taxnum) + ON (cust_bill_pkg.billpkgnum = pkg_tax_exempt.billpkgnum) + LEFT JOIN ($pkg_credited) AS pkg_credited + ON (cust_bill_pkg.billpkgnum = pkg_credited.billpkgnum) + LEFT JOIN cust_main_county + ON (COALESCE(pkg_tax.taxnum, pkg_tax_exempt.taxnum) = cust_main_county.taxnum) $join_cust_pkg $where AND $nottax $group"; $all_sql{taxable} = "$select_all - SUM(cust_bill_pkg.setup + cust_bill_pkg.recur - COALESCE(exempt_charged, 0)) - FROM cust_main_county - JOIN ($pkg_tax) AS pkg_tax USING (taxnum) - JOIN cust_bill_pkg USING (billpkgnum) + SUM( + cust_bill_pkg.setup + cust_bill_pkg.recur + - COALESCE(exempt_charged, 0) + - COALESCE(credited, 0) + ) + FROM cust_bill_pkg + LEFT JOIN ($pkg_tax) AS pkg_tax + ON (cust_bill_pkg.billpkgnum = pkg_tax.billpkgnum) LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt - ON (pkg_tax_exempt.billpkgnum = cust_bill_pkg.billpkgnum - AND pkg_tax_exempt.taxnum = cust_main_county.taxnum) + ON (cust_bill_pkg.billpkgnum = pkg_tax_exempt.billpkgnum) + LEFT JOIN ($pkg_credited) AS pkg_credited + ON (cust_bill_pkg.billpkgnum = pkg_credited.billpkgnum) + LEFT JOIN cust_main_county + ON (COALESCE(pkg_tax.taxnum, pkg_tax_exempt.taxnum) = cust_main_county.taxnum) $join_cust_pkg $where AND $nottax $group_all"; $sql{taxable} =~ s/EXEMPT_WHERE//; # unrestricted $all_sql{taxable} =~ s/EXEMPT_WHERE//; + # estimated tax (taxable * rate) + $sql{estimated} = "$select + SUM(cust_main_county.tax / 100 * + ( cust_bill_pkg.setup + cust_bill_pkg.recur + - COALESCE(exempt_charged, 0) + - COALESCE(credited, 0) + ) + ) + FROM cust_bill_pkg + LEFT JOIN ($pkg_tax) AS pkg_tax + ON (cust_bill_pkg.billpkgnum = pkg_tax.billpkgnum) + LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt + ON (cust_bill_pkg.billpkgnum = pkg_tax_exempt.billpkgnum) + LEFT JOIN ($pkg_credited) AS pkg_credited + ON (cust_bill_pkg.billpkgnum = pkg_credited.billpkgnum) + LEFT JOIN cust_main_county + ON (COALESCE(pkg_tax.taxnum, pkg_tax_exempt.taxnum) = cust_main_county.taxnum) + $join_cust_pkg $where AND $nottax + $group"; + + $all_sql{estimated} = "$select_all + SUM(cust_main_county.tax / 100 * + ( cust_bill_pkg.setup + cust_bill_pkg.recur + - COALESCE(exempt_charged, 0) + - COALESCE(credited, 0) + ) + ) + FROM cust_bill_pkg + LEFT JOIN ($pkg_tax) AS pkg_tax + ON (cust_bill_pkg.billpkgnum = pkg_tax.billpkgnum) + LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt + ON (cust_bill_pkg.billpkgnum = pkg_tax_exempt.billpkgnum) + LEFT JOIN ($pkg_credited) AS pkg_credited + ON (cust_bill_pkg.billpkgnum = pkg_credited.billpkgnum) + LEFT JOIN cust_main_county + ON (COALESCE(pkg_tax.taxnum, pkg_tax_exempt.taxnum) = cust_main_county.taxnum) + $join_cust_pkg $where AND $nottax + $group_all"; + # there isn't one for 'sales', because we calculate sales by adding up # the taxable and exempt columns. - + # TAX QUERIES (billed tax, credited tax) # ----------- @@ -261,6 +323,9 @@ sub report_internal { my %data; my %total; + # note that we use keys(%sql) here and keys(%all_sql) later. nothing + # obligates us to use the same set of variables for the total query + # as for the individual category queries foreach my $k (keys(%sql)) { my $stmt = $sql{$k}; warn "\n".uc($k).":\n".$stmt."\n" if $DEBUG; @@ -304,6 +369,40 @@ sub report_internal { # ] # non-requested grouping levels simply collapse into key = '' + # the much-maligned "out of taxable region"... + # find sales that are not linked to any tax with this name + # but are still inside the date range/agent criteria. + # + # This doesn't use $select_all/$group_all because we want a single number, + # not a breakdown by pkgclass. Unless someone needs that eventually, + # in which case we'll turn it into an %all_sql query. + + my $outside_where = + "WHERE cust_bill._date >= $beginning AND cust_bill._date <= $ending"; + if ( $agentnum ) { + $outside_where .= " AND cust_main.agentnum = $agentnum"; + } + my $sql_outside = "SELECT SUM(cust_bill_pkg.setup + cust_bill_pkg.recur) + FROM cust_bill_pkg + $join_cust_pkg + $outside_where + AND $nottax + AND NOT EXISTS( + SELECT 1 FROM cust_tax_exempt_pkg + JOIN cust_main_county USING (taxnum) + WHERE cust_tax_exempt_pkg.billpkgnum = cust_bill_pkg.billpkgnum + AND COALESCE(cust_main_county.taxname,'Tax') = '$taxname' + ) + AND NOT EXISTS( + SELECT 1 FROM cust_bill_pkg_tax_location + JOIN cust_main_county USING (taxnum) + WHERE cust_bill_pkg_tax_location.taxable_billpkgnum = cust_bill_pkg.billpkgnum + AND COALESCE(cust_main_county.taxname,'Tax') = '$taxname' + ) + "; + warn "\nOUTSIDE:\n$sql_outside\n" if $DEBUG; + my $total_outside = FS::Record->scalar_sql($sql_outside); + my %taxrates; foreach my $tax ( qsearch('cust_main_county', { @@ -320,6 +419,7 @@ sub report_internal { 'data' => \%data, 'total' => \%total, 'taxrates' => \%taxrates, + 'outside' => $total_outside, }, $class; } @@ -411,8 +511,6 @@ sub table { $this_row{rate} = 'variable'; } elsif ( $rate > 0 ) { $this_row{rate} = sprintf('%.2f', $rate); - $this_row{estimated} = - sprintf('%.2f', $this_row{taxable} * $rate / 100); } push @rows, \%this_row; } @@ -435,7 +533,6 @@ sub title { if ( $self->{opt}->{agentnum} ) { my $agent = qsearchs('agent', { agentnum => $self->{opt}->{agentnum} }); $string .= $agent->agent . ' '; - warn $string; } $string .= 'Tax Report: '; # XXX localization if ( $self->{opt}->{beginning} ) {