use FS::part_pkg;
use FS::cust_tax_exempt;
use FS::cust_tax_exempt_pkg;
+use FS::upgrade_journal;
@EXPORT_OK = qw( regionselector );
=item recurtax - if 'Y', this tax does not apply to recurring fees
+=item source - the tax lookup method that created this tax record. For records
+created manually, this will be null.
+
=back
=head1 METHODS
sub check {
my $self = shift;
+ $self->trim_whitespace(qw(district city county state country));
+ $self->set('city', uc($self->get('city'))); # also county?
+
$self->exempt_amount(0) unless $self->exempt_amount;
$self->ut_numbern('taxnum')
|| $self->ut_textn('taxname')
|| $self->ut_enum('setuptax', [ '', 'Y' ] )
|| $self->ut_enum('recurtax', [ '', 'Y' ] )
+ || $self->ut_textn('source')
|| $self->SUPER::check
;
my $cust_bill = $taxables->[0]->cust_bill;
my $custnum = $cust_bill ? $cust_bill->custnum : $opt{'custnum'};
my $invoice_time = $cust_bill ? $cust_bill->_date : $opt{'invoice_time'};
- my $cust_main = FS::cust_main->by_key($custnum) if $custnum > 0;
+ 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) {
#}
# 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.
my $part_pkg = $cust_bill_pkg->part_pkg;
my $part_fee = $cust_bill_pkg->part_fee;
- my $locationnum = $cust_pkg
- ? $cust_pkg->locationnum
- : $cust_main->bill_locationnum;
+ 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
}
+sub _upgrade_data {
+ my $class = shift;
+ # assume taxes in Washington with district numbers, and null name, or
+ # named 'sales tax', are looked up via the wa_sales method. mark them.
+ my $journal = 'cust_main_county__source_wa_sales';
+ if (!FS::upgrade_journal->is_done($journal)) {
+ my @taxes = qsearch({
+ 'table' => 'cust_main_county',
+ 'extra_sql' => " WHERE tax > 0 AND country = 'US' AND state = 'WA'".
+ " AND district IS NOT NULL AND ( taxname IS NULL OR ".
+ " taxname ~* 'sales tax' )",
+ });
+ if ( @taxes ) {
+ warn "Flagging Washington state sales taxes: ".scalar(@taxes)." records.\n";
+ foreach (@taxes) {
+ $_->set('source', 'wa_sales');
+ my $error = $_->replace;
+ die $error if $error;
+ }
+ }
+ FS::upgrade_journal->set_done($journal);
+ }
+ # trim whitespace and convert to uppercase in the 'city' field.
+ foreach my $record (qsearch({
+ table => 'cust_main_county',
+ extra_sql => " WHERE city LIKE ' %' OR city LIKE '% ' OR city != UPPER(city)",
+ })) {
+ # any with-trailing-space records probably duplicate other records
+ # from the same city, and if we just fix the record in place, we'll
+ # create an exact duplicate.
+ # so find the record this one would duplicate, and merge them.
+ $record->check; # trims whitespace
+ my %match = map { $_ => $record->get($_) }
+ qw(city county state country district taxname taxclass);
+ my $other = qsearchs('cust_main_county', \%match);
+ if ($other) {
+ my $new_taxnum = $other->taxnum;
+ my $old_taxnum = $record->taxnum;
+ if ($other->tax != $record->tax or
+ $other->exempt_amount != $record->exempt_amount) {
+ # don't assume these are the same.
+ warn "Found duplicate taxes (#$new_taxnum and #$old_taxnum) but they have different rates and can't be merged.\n";
+ } else {
+ warn "Merging tax #$old_taxnum into #$new_taxnum\n";
+ foreach my $table (qw(
+ cust_bill_pkg_tax_location
+ cust_bill_pkg_tax_location_void
+ cust_tax_exempt_pkg
+ cust_tax_exempt_pkg_void
+ )) {
+ foreach my $row (qsearch($table, { 'taxnum' => $old_taxnum })) {
+ $row->set('taxnum' => $new_taxnum);
+ my $error = $row->replace;
+ die $error if $error;
+ }
+ }
+ my $error = $record->delete;
+ die $error if $error;
+ }
+ } else {
+ # else there is no record this one duplicates, so just fix it
+ my $error = $record->replace;
+ die $error if $error;
+ }
+ } # foreach $record
+ '';
+}
+
=back
=head1 BUGS