use FS::cust_bill_pkg_tax_rate_location_void;
use FS::cust_tax_exempt_pkg_void;
+use FS::Cursor;
+
$DEBUG = 0;
$me = '[FS::cust_bill_pkg]';
sub tax_location {
my $self = shift;
- FS::cust_location->by_key($self->tax_locationnum);
+ if ( $self->pkgnum ) { # normal sales
+ return $self->cust_pkg->tax_location;
+ } elsif ( $self->feepart ) { # fees
+ return $self->cust_bill->cust_main->ship_location;
+ } else { # taxes
+ return;
+ }
}
=item part_X
sub part_X {
my $self = shift;
- if ( $self->override_pkgpart ) {
- return FS::part_pkg->by_key($self->override_pkgpart);
+ if ( $self->pkgpart_override ) {
+ return FS::part_pkg->by_key($self->pkgpart_override);
} elsif ( $self->pkgnum ) {
return $self->cust_pkg->part_pkg;
} elsif ( $self->feepart ) {
' WHERE cust_bill_pkg.invnum = cust_bill.invnum'.
' AND exempt_monthly IS NULL';
- my @invnums = map { $_->invnum } qsearch({
- select => 'cust_bill.invnum',
+ my %all_tax_names = (
+ '' => 1,
+ 'Tax' => 1,
+ map { $_->taxname => 1 }
+ qsearch('h_cust_main_county', { taxname => { op => '!=', value => '' }})
+ );
+
+ my $search = FS::Cursor->new({
table => 'cust_bill',
hashref => {},
extra_sql => "WHERE NOT EXISTS($sub_has_tax_link) ".
$date_where,
});
- print "Processing ".scalar(@invnums)." invoices...\n";
+#print "Processing ".scalar(@invnums)." invoices...\n";
my $committed;
INVOICE:
- foreach my $invnum (@invnums) {
+ while (my $cust_bill = $search->fetch) {
+ my $invnum = $cust_bill->invnum;
$committed = 0;
print STDERR "Invoice #$invnum\n";
my $pre = '';
# invoice date-of-insertion. (Not necessarily the invoice date.)
my $date = $h_cust_bill->history_date;
my $h_cust_main = qsearchs('h_cust_main',
- { custnum => $custnum },
+ { custnum => $custnum },
FS::h_cust_main->sql_h_searchs($date)
);
if (!$h_cust_main ) {
# This is a historical customer record, so it has a historical address.
# If there's no cust_location matching this custnum and address (there
# probably isn't), create one.
- $pre = 'ship_' if $use_ship and length($h_cust_main->get('ship_last'));
- my %hash = map { $_ => $h_cust_main->get($pre.$_) }
- FS::cust_main->location_fields;
- # not really needed for this, and often result in duplicate locations
- delete @hash{qw(censustract censusyear latitude longitude coord_auto)};
-
- $hash{custnum} = $h_cust_main->custnum;
- my $tax_loc = FS::cust_location->new(\%hash);
- my $error = $tax_loc->find_or_insert || $tax_loc->disable_if_unused;
- if ( $error ) {
- warn "couldn't create historical location record for cust#".
- $h_cust_main->custnum.": $error\n";
- next INVOICE;
+ my $tax_loc;
+ if ( $h_cust_main->bill_locationnum ) {
+ # the location has already been upgraded
+ if ($use_ship) {
+ $tax_loc = $h_cust_main->ship_location;
+ } else {
+ $tax_loc = $h_cust_main->bill_location;
+ }
+ } else {
+ $pre = 'ship_' if $use_ship and length($h_cust_main->get('ship_last'));
+ my %hash = map { $_ => $h_cust_main->get($pre.$_) }
+ FS::cust_main->location_fields;
+ # not really needed for this, and often result in duplicate locations
+ delete @hash{qw(censustract censusyear latitude longitude coord_auto)};
+
+ $hash{custnum} = $h_cust_main->custnum;
+ $tax_loc = FS::cust_location->new(\%hash);
+ my $error = $tax_loc->find_or_insert || $tax_loc->disable_if_unused;
+ if ( $error ) {
+ warn "couldn't create historical location record for cust#".
+ $h_cust_main->custnum.": $error\n";
+ next INVOICE;
+ }
}
my $exempt_cust = 1 if $h_cust_main->tax;
- # Get any per-customer taxname exemptions that were in effect.
- my %exempt_cust_taxname = map {
- $_->taxname => 1
- } qsearch('h_cust_main_exemption', { 'custnum' => $custnum },
- FS::h_cust_main_exemption->sql_h_searchs($date)
- );
-
# classify line items
my @tax_items;
my %nontax_items; # taxclass => array of cust_bill_pkg
push @{ $nontax_items{$taxclass} }, $item;
}
}
+
printf("%d tax items: \$%.2f\n", scalar(@tax_items), map {$_->setup} @tax_items)
if @tax_items;
+ # Get any per-customer taxname exemptions that were in effect.
+ my %exempt_cust_taxname;
+ foreach (keys %all_tax_names) {
+ my $h_exemption = qsearchs('h_cust_main_exemption', {
+ 'custnum' => $custnum,
+ 'taxname' => $_,
+ },
+ FS::h_cust_main_exemption->sql_h_searchs($date, $date)
+ );
+ if ($h_exemption) {
+ $exempt_cust_taxname{ $_ } = 1;
+ }
+ }
+
# Use a variation on the procedure in
# FS::cust_main::Billing::_handle_taxes to identify taxes that apply
# to this bill.
my @loc_keys = qw( district city county state country );
- my %taxhash = map { $_ => $h_cust_main->get($pre.$_) } @loc_keys;
+ my %taxhash = map { $_ => $tax_loc->get($pre.$_) } @loc_keys;
my %taxdef_by_name; # by name, and then by taxclass
my %est_tax; # by name, and then by taxclass
my %taxable_items; # by taxnum, and then an array
if ( !exists( $taxdef_by_name{$taxname} ) ) {
# then we didn't find any applicable taxes with this name
- warn "no definition found for tax item '$taxname'.\n".
- '('.join(' ', @hash{qw(country state county city district)}).")\n";
+ warn "no definition found for tax item '$taxname', custnum $custnum\n";
# possibly all of these should be "next TAX_ITEM", but whole invoices
# are transaction protected and we can go back and retry them.
next INVOICE;
my $i = 0;
my $nlinks = scalar(@tax_links);
if ( $nlinks ) {
- while (int($cents_remaining) > 0) {
+ # ensure that it really is an integer
+ $cents_remaining = sprintf('%.0f', $cents_remaining);
+ while ($cents_remaining > 0) {
$tax_links[$i % $nlinks]->{cents} += 1;
$cents_remaining--;
$i++;
});
# call it kind of like a class method, not that it matters much
$job->insert($class, 's' => str2time('2012-01-01'));
+ # if there's a customer location upgrade queued also, wait for it to
+ # finish
+ my $location_job = qsearchs('queue', {
+ job => 'FS::cust_main::Location::process_upgrade_location'
+ });
+ if ( $location_job ) {
+ $job->depend_insert($location_job->jobnum);
+ }
# Then mark the upgrade as done, so that we don't queue the job twice
# and somehow run two of them concurrently.
FS::upgrade_journal->set_done($upgrade);