X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fcust_main_county.pm;h=172eac9c627ff92d89b64f42388f7a4ec23d1740;hb=c545a57d21341c49674defed65a4243f07b5ebaf;hp=383360b7b0b04bd2b8c268d82ff046a2152ffda3;hpb=6cd87c0d3b5280446301c647fa5f1ec5a593fa3f;p=freeside.git diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm index 383360b7b..172eac9c6 100644 --- a/FS/FS/cust_main_county.pm +++ b/FS/FS/cust_main_county.pm @@ -1,10 +1,27 @@ package FS::cust_main_county; use strict; -use vars qw( @ISA ); -use FS::Record; +use vars qw( @ISA @EXPORT_OK $conf + @cust_main_county %cust_main_county $countyflag ); +use Exporter; +use FS::Record qw( qsearch dbh ); +use FS::cust_bill_pkg; +use FS::cust_bill; +use FS::cust_pkg; +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 = (); +$countyflag = ''; + +#ask FS::UID to run this stuff for us later +$FS::UID::callback{'FS::cust_main_county'} = sub { + $conf = new FS::Conf; +}; =head1 NAME @@ -25,6 +42,9 @@ FS::cust_main_county - Object methods for cust_main_county objects $error = $record->check; + ($county_html, $state_html, $country_html) = + FS::cust_main_county::regionselector( $county, $state, $country ); + =head1 DESCRIPTION An FS::cust_main_county object represents a tax rate, defined by locale. @@ -43,6 +63,16 @@ currently supported: =item tax - percentage +=item taxclass + +=item exempt_amount + +=item taxname - if defined, printed on invoices instead of "Tax" + +=item setuptax - if 'Y', this tax does not apply to setup fees + +=item recurtax - if 'Y', this tax does not apply to recurring fees + =back =head1 METHODS @@ -83,23 +113,312 @@ methods. sub check { my $self = shift; + $self->exempt_amount(0) unless $self->exempt_amount; + $self->ut_numbern('taxnum') - || $self->ut_textn('state') + || $self->ut_anything('state') || $self->ut_textn('county') || $self->ut_text('country') || $self->ut_float('tax') - ; + || $self->ut_textn('taxclass') # ... + || $self->ut_money('exempt_amount') + || $self->ut_textn('taxname') + || $self->ut_enum('setuptax', [ '', 'Y' ] ) + || $self->ut_enum('recurtax', [ '', 'Y' ] ) + || $self->SUPER::check + ; + +} + +sub taxname { + my $self = shift; + if ( $self->dbdef_table->column('taxname') ) { + return $self->setfield('taxname', $_[0]) if @_; + return $self->getfield('taxname'); + } + return ''; +} + +sub setuptax { + my $self = shift; + if ( $self->dbdef_table->column('setuptax') ) { + return $self->setfield('setuptax', $_[0]) if @_; + return $self->getfield('setuptax'); + } + return ''; +} + +sub recurtax { + my $self = shift; + if ( $self->dbdef_table->column('recurtax') ) { + return $self->setfield('recurtax', $_[0]) if @_; + return $self->getfield('recurtax'); + } + return ''; +} + +=item taxline CUST_BILL_PKG, ... +Returns a listref of a name and an amount of tax calculated for the list of +packages. Returns a scalar error message on error. + +=cut + +sub taxline { + my $self = shift; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $name = $self->taxname || 'Tax'; + my $amount = 0; + + foreach my $cust_bill_pkg (@_) { + + my $cust_bill = $cust_bill_pkg->cust_pkg->cust_bill; + my $part_pkg = $cust_bill_pkg->part_pkg; + + my $taxable_charged = 0; + $taxable_charged += $cust_bill_pkg->setup + unless $part_pkg->setuptax =~ /^Y$/i + || $self->setuptax =~ /^Y$/i; + $taxable_charged += $cust_bill_pkg->recur + unless $part_pkg->recurtax =~ /^Y$/i + || $self->recurtax =~ /^Y$/i; + + return [ $name, 0 ] + unless $taxable_charged; + + if ( $self->exempt_amount && $self->exempt_amount > 0 ) { + #my ($mon,$year) = (localtime($cust_bill_pkg->sdate) )[4,5]; + my ($mon,$year) = + (localtime( $cust_bill_pkg->sdate || $cust_bill->_date ) )[4,5]; + $mon++; + my $freq = $part_pkg->freq || 1; + if ( $freq !~ /(\d+)$/ ) { + $dbh->rollback if $oldAutoCommit; + return "daily/weekly package definitions not (yet?)". + " compatible with monthly tax exemptions"; + } + my $taxable_per_month = + sprintf("%.2f", $taxable_charged / $freq ); + + #call the whole thing off if this customer has any old + #exemption records... + my @cust_tax_exempt = + qsearch( 'cust_tax_exempt' => { custnum=> $cust_bill->custnum } ); + if ( @cust_tax_exempt ) { + $dbh->rollback if $oldAutoCommit; + return + 'this customer still has old-style tax exemption records; '. + 'run bin/fs-migrate-cust_tax_exempt?'; + } + + foreach my $which_month ( 1 .. $freq ) { + + #maintain the new exemption table now + my $sql = " + SELECT SUM(amount) + FROM cust_tax_exempt_pkg + LEFT JOIN cust_bill_pkg USING ( billpkgnum ) + LEFT JOIN cust_bill USING ( invnum ) + WHERE custnum = ? + AND taxnum = ? + AND year = ? + AND month = ? + "; + my $sth = dbh->prepare($sql) or do { + $dbh->rollback if $oldAutoCommit; + return "fatal: can't lookup exising exemption: ". dbh->errstr; + }; + $sth->execute( + $cust_bill->custnum, + $self->taxnum, + 1900+$year, + $mon, + ) or do { + $dbh->rollback if $oldAutoCommit; + return "fatal: can't lookup exising exemption: ". dbh->errstr; + }; + my $existing_exemption = $sth->fetchrow_arrayref->[0] || 0; + + my $remaining_exemption = + $self->exempt_amount - $existing_exemption; + if ( $remaining_exemption > 0 ) { + my $addl = $remaining_exemption > $taxable_per_month + ? $taxable_per_month + : $remaining_exemption; + $taxable_charged -= $addl; + + my $cust_tax_exempt_pkg = new FS::cust_tax_exempt_pkg ( { + 'billpkgnum' => $cust_bill_pkg->billpkgnum, + 'taxnum' => $self->taxnum, + 'year' => 1900+$year, + 'month' => $mon, + 'amount' => sprintf("%.2f", $addl ), + } ); + my $error = $cust_tax_exempt_pkg->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "fatal: can't insert cust_tax_exempt_pkg: $error"; + } + } # if $remaining_exemption > 0 + + #++ + $mon++; + #until ( $mon < 12 ) { $mon -= 12; $year++; } + until ( $mon < 13 ) { $mon -= 12; $year++; } + + } #foreach $which_month + + } #if $tax->exempt_amount + + $taxable_charged = sprintf( "%.2f", $taxable_charged); + + $amount += $taxable_charged * $self->tax / 100 + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + return [ $name, $amount ] } =back -=head1 VERSION +=head1 SUBROUTINES + +=over 4 + +=item regionselector [ COUNTY STATE COUNTRY [ PREFIX [ ONCHANGE [ DISABLED ] ] ] ] -$Id: cust_main_county.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $ +=cut + +sub regionselector { + my ( $selected_county, $selected_state, $selected_country, + $prefix, $onchange, $disabled ) = @_; + + $prefix = '' unless defined $prefix; + + $countyflag = 0; + +# unless ( @cust_main_county ) { #cache + @cust_main_county = qsearch('cust_main_county', {} ); + foreach my $c ( @cust_main_county ) { + $countyflag=1 if $c->county; + #push @{$cust_main_county{$c->country}{$c->state}}, $c->county; + $cust_main_county{$c->country}{$c->state}{$c->county} = 1; + } +# } + $countyflag=1 if $selected_county; + + my $script_html = < + function opt(what,value,text) { + var optionName = new Option(text, value, false, false); + var length = what.length; + what.options[length] = optionName; + } + function ${prefix}country_changed(what) { + country = what.options[what.selectedIndex].text; + for ( var i = what.form.${prefix}state.length; i >= 0; i-- ) + what.form.${prefix}state.options[i] = null; +END + #what.form.${prefix}state.options[0] = new Option('', '', false, true); + + foreach my $country ( sort keys %cust_main_county ) { + $script_html .= "\nif ( country == \"$country\" ) {\n"; + foreach my $state ( sort keys %{$cust_main_county{$country}} ) { + ( my $dstate = $state ) =~ s/[\n\r]//g; + my $text = $dstate || '(n/a)'; + $script_html .= qq!opt(what.form.${prefix}state, "$dstate", "$text");\n!; + } + $script_html .= "}\n"; + } + + $script_html .= <= 0; i-- ) + what.form.${prefix}county.options[i] = null; +END + + foreach my $country ( sort keys %cust_main_county ) { + $script_html .= "\nif ( country == \"$country\" ) {\n"; + foreach my $state ( sort keys %{$cust_main_county{$country}} ) { + $script_html .= "\nif ( state == \"$state\" ) {\n"; + #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) { + foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) { + my $text = $county || '(n/a)'; + $script_html .= + qq!opt(what.form.${prefix}county, "$county", "$text");\n!; + } + $script_html .= "}\n"; + } + $script_html .= "}\n"; + } + } + + $script_html .= < +END + + my $county_html = $script_html; + if ( $countyflag ) { + $county_html .= qq!'; + } else { + $county_html .= + qq!!; + } + + my $state_html = qq!'; + + $state_html .= ''; + + my $country_html = qq!'; + + ($county_html, $state_html, $country_html); + +} + +=back =head1 BUGS +regionselector? putting web ui components in here? they should probably live +somewhere else... + =head1 SEE ALSO L, L, L, schema.html from the base