X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main_county.pm;h=652ff33aeb05e84fe2ac75fae6af79850012baac;hb=53ed077b4901be22b3ca5405a093364b6f6b6407;hp=a61d67e11508fe68b1665ff5300594254b2631e2;hpb=196d15248930f3e54811a8b46b5ede47db678e19;p=freeside.git diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm index a61d67e11..652ff33ae 100644 --- a/FS/FS/cust_main_county.pm +++ b/FS/FS/cust_main_county.pm @@ -1,7 +1,8 @@ package FS::cust_main_county; +use base qw( FS::Record ); use strict; -use vars qw( @ISA @EXPORT_OK $conf +use vars qw( @EXPORT_OK $conf @cust_main_county %cust_main_county $countyflag ); # $cityflag ); use Exporter; use FS::Record qw( qsearch qsearchs dbh ); @@ -12,7 +13,6 @@ use FS::part_pkg; use FS::cust_tax_exempt; use FS::cust_tax_exempt_pkg; -@ISA = qw( FS::Record ); @EXPORT_OK = qw( regionselector ); @cust_main_county = (); @@ -244,7 +244,7 @@ are inserted. In addition to calculating the tax for the line items, this will calculate any appropriate tax exemptions and attach them to the line items. -Options may include 'custnum' and 'invoice_date' in case the cust_bill_pkg +Options may include 'custnum' and 'invoice_time' in case the cust_bill_pkg objects belong to an invoice that hasn't been inserted yet. Options may include 'exemptions', an arrayref of L @@ -276,26 +276,30 @@ sub taxline { my $cust_bill = $taxables->[0]->cust_bill; my $custnum = $cust_bill ? $cust_bill->custnum : $opt{'custnum'}; - my $invoice_date = $cust_bill ? $cust_bill->_date : $opt{'invoice_date'}; - my $cust_main = FS::cust_main->by_key($custnum) if $custnum > 0; - if (!$cust_main) { - # better way to handle this? should we just assume that it's taxable? - die "unable to calculate taxes for an unknown customer\n"; - } + my $invoice_time = $cust_bill ? $cust_bill->_date : $opt{'invoice_time'}; + my $cust_main = FS::cust_main->by_key($custnum) if $custnum; + # (to avoid complications with estimated tax on quotations, assume it's + # taxable if there is no customer) + #if (!$cust_main) { + #die "unable to calculate taxes for an unknown customer\n"; + #} # set a flag if the customer is tax-exempt - my $exempt_cust; + my ($exempt_cust, $exempt_cust_taxname); my $conf = FS::Conf->new; - if ( $conf->exists('cust_class-tax_exempt') ) { - my $cust_class = $cust_main->cust_class; - $exempt_cust = $cust_class->tax if $cust_class; - } else { - $exempt_cust = $cust_main->tax; - } + if ( $cust_main ) { + if ( $conf->exists('cust_class-tax_exempt') ) { + my $cust_class = $cust_main->cust_class; + $exempt_cust = $cust_class->tax if $cust_class; + } else { + $exempt_cust = $cust_main->tax; + } - # set a flag if the customer is exempt from this tax here - my $exempt_cust_taxname = $cust_main->tax_exemption($self->taxname) - if $self->taxname; + # set a flag if the customer is exempt from this tax here + if ( $self->taxname ) { + $exempt_cust_taxname = $cust_main->tax_exemption($self->taxname); + } + } # Gather any exemptions that are already attached to these cust_bill_pkgs # so that we can deduct them from the customer's monthly limit. @@ -313,9 +317,14 @@ sub taxline { my @tax_location; foreach my $cust_bill_pkg (@$taxables) { + # careful... may be a cust_bill_pkg or a quotation_pkg my $cust_pkg = $cust_bill_pkg->cust_pkg; my $part_pkg = $cust_bill_pkg->part_pkg; + my $part_fee = $cust_bill_pkg->part_fee; + + my $locationnum = $cust_bill_pkg->tax_locationnum + || $cust_main->ship_locationnum; my @new_exemptions; my $taxable_charged = $cust_bill_pkg->setup + $cust_bill_pkg->recur @@ -341,8 +350,13 @@ sub taxline { } - if ( ($part_pkg->setuptax eq 'Y' or $self->setuptax eq 'Y') - and $cust_bill_pkg->setup > 0 and $taxable_charged > 0 ) { + my $setup_exempt = ( ($part_fee and not $part_fee->taxable) + or ($part_pkg and $part_pkg->setuptax) + or $self->setuptax ); + + if ( $setup_exempt + and $cust_bill_pkg->setup > 0 + and $taxable_charged > 0 ) { push @new_exemptions, FS::cust_tax_exempt_pkg->new({ amount => $cust_bill_pkg->setup, @@ -351,8 +365,14 @@ sub taxline { $taxable_charged -= $cust_bill_pkg->setup; } - if ( ($part_pkg->recurtax eq 'Y' or $self->recurtax eq 'Y') - and $cust_bill_pkg->recur > 0 and $taxable_charged > 0 ) { + + my $recur_exempt = ( ($part_fee and not $part_fee->taxable) + or ($part_pkg and $part_pkg->recurtax) + or $self->recurtax ); + + if ( $recur_exempt + and $cust_bill_pkg->recur > 0 + and $taxable_charged > 0 ) { push @new_exemptions, FS::cust_tax_exempt_pkg->new({ amount => $cust_bill_pkg->recur, @@ -363,23 +383,41 @@ sub taxline { } if ( $self->exempt_amount && $self->exempt_amount > 0 - and $taxable_charged > 0 ) { - #my ($mon,$year) = (localtime($cust_bill_pkg->sdate) )[4,5]; - my ($mon,$year) = - (localtime( $cust_bill_pkg->sdate || $invoice_date ) )[4,5]; - $mon++; - $year += 1900; - my $freq = $cust_bill_pkg->freq; - unless ($freq) { - $freq = $part_pkg->freq || 1; # less trustworthy fallback - } - if ( $freq !~ /(\d+)$/ ) { - $dbh->rollback if $oldAutoCommit; - return "daily/weekly package definitions not (yet?)". - " compatible with monthly tax exemptions"; + and $taxable_charged > 0 + and $cust_main ) { + + # XXX monthly exemptions currently don't work on quotations + + # If the billing period extends across multiple calendar months, + # there may be several months of exemption available. + my $sdate = $cust_bill_pkg->sdate || $invoice_time; + my $start_month = (localtime($sdate))[4] + 1; + my $start_year = (localtime($sdate))[5] + 1900; + my $edate = $cust_bill_pkg->edate || $invoice_time; + my $end_month = (localtime($edate))[4] + 1; + my $end_year = (localtime($edate))[5] + 1900; + + # If the partial last month + partial first month <= one month, + # don't use the exemption in the last month + # (unless the last month is also the first month, e.g. one-time + # charges) + if ( (localtime($sdate))[3] >= (localtime($edate))[3] + and ($start_month != $end_month or $start_year != $end_year) + ) { + $end_month--; + if ( $end_month == 0 ) { + $end_year--; + $end_month = 12; + } } - my $taxable_per_month = - sprintf("%.2f", $taxable_charged / $freq ); + + # number of months of exemption available + my $freq = ($end_month - $start_month) + + ($end_year - $start_year) * 12 + + 1; + + # divide equally among all of them + my $permonth = sprintf('%.2f', $taxable_charged / $freq); #call the whole thing off if this customer has any old #exemption records... @@ -392,9 +430,15 @@ sub taxline { 'run bin/fs-migrate-cust_tax_exempt?'; } - foreach my $which_month ( 1 .. $freq ) { - - #maintain the new exemption table now + my ($mon, $year) = ($start_month, $start_year); + while ($taxable_charged > 0.005 and + ($year < $end_year or + ($year == $end_year and $mon <= $end_month) + ) + ) { + + # find the sum of the exemption used by this customer, for this tax, + # in this month my $sql = " SELECT SUM(amount) FROM cust_tax_exempt_pkg @@ -408,7 +452,7 @@ sub taxline { "; my $sth = dbh->prepare($sql) or do { $dbh->rollback if $oldAutoCommit; - return "fatal: can't lookup exising exemption: ". dbh->errstr; + return "fatal: can't lookup existing exemption: ". dbh->errstr; }; $sth->execute( $custnum, @@ -417,10 +461,11 @@ sub taxline { $mon, ) or do { $dbh->rollback if $oldAutoCommit; - return "fatal: can't lookup exising exemption: ". dbh->errstr; + return "fatal: can't lookup existing exemption: ". dbh->errstr; }; my $existing_exemption = $sth->fetchrow_arrayref->[0] || 0; + # add any exemption we're already using for another line item foreach ( grep { $_->taxnum == $self->taxnum && $_->exempt_monthly eq 'Y' && $_->month == $mon && @@ -430,13 +475,15 @@ sub taxline { { $existing_exemption += $_->amount; } - + my $remaining_exemption = $self->exempt_amount - $existing_exemption; if ( $remaining_exemption > 0 ) { - my $addl = $remaining_exemption > $taxable_per_month - ? $taxable_per_month + my $addl = $remaining_exemption > $permonth + ? $permonth : $remaining_exemption; + $addl = $taxable_charged if $addl > $taxable_charged; + push @new_exemptions, FS::cust_tax_exempt_pkg->new({ amount => sprintf('%.2f', $addl), exempt_monthly => 'Y', @@ -445,7 +492,6 @@ sub taxline { }); $taxable_charged -= $addl; } - last if $taxable_charged < 0.005; # if they're using multiple months of exemption for a multi-month # package, then record the exemptions in separate months $mon++; @@ -454,8 +500,8 @@ sub taxline { $year++; } - } #foreach $which_month - } # if exempt_amount + } + } # if exempt_amount and $cust_main $_->taxnum($self->taxnum) foreach @new_exemptions; @@ -472,7 +518,7 @@ sub taxline { 'taxtype' => ref($self), 'cents' => $this_tax_cents, 'pkgnum' => $cust_bill_pkg->pkgnum, - 'locationnum' => $cust_bill_pkg->cust_pkg->tax_locationnum, + 'locationnum' => $locationnum, 'taxable_cust_bill_pkg' => $cust_bill_pkg, 'tax_cust_bill_pkg' => $tax_item, });