- if ( $recur_amount > 0 or $setup_amount > 0 ) {
- $quotation_pkg_tax->set('setup_amount', sprintf('%.2f', $setup_amount));
- $quotation_pkg_tax->set('recur_amount', sprintf('%.2f', $recur_amount));
-
- my $error = $quotation_pkg_tax->insert;
- if ($error) {
- $dbh->rollback if $oldAutoCommit;
- die "error recording '".$tax_def->taxname .
- "' for pkgpart '".$pkg->pkgpart."': $error\n";
- } # if $error
- } # else there are no non-zero taxes; continue
- } # foreach $tax_def
- } # foreach $pkg
-
- $dbh->commit if $oldAutoCommit;
- '';
+ my $links = $cust_bill_pkg->get('cust_bill_pkg_tax_location') ||
+ $cust_bill_pkg->get('cust_bill_pkg_tax_rate_location') ||
+ [];
+ # breadth-first unrolled recursion
+ while (my $tax_link = shift @$links) {
+ my $target = $cust_bill_pkg{ $tax_link->taxable_billpkgnum }
+ or die "$me unable to resolve tax link (taxnum ".$tax_link->taxnum.")\n";
+ if ($target->pkgnum) {
+ my $quotationpkgnum = $quotationpkgnum_of{$target->pkgnum};
+ # create this if there isn't one yet
+ my $qpt =
+ $quotation_pkg_tax{$quotationpkgnum}{$tax_link->taxnum} ||=
+ FS::quotation_pkg_tax->new({
+ quotationpkgnum => $quotationpkgnum,
+ itemdesc => $cust_bill_pkg->itemdesc,
+ taxnum => $tax_link->taxnum,
+ taxtype => $tax_link->taxtype,
+ setup_amount => 0,
+ recur_amount => 0,
+ });
+ if ( $i == 0 ) { # first invoice
+ $qpt->set('setup_amount', $qpt->setup_amount + $tax_link->amount);
+ } else { # subsequent invoices
+ # this isn't perfectly accurate, but that's why it's an estimate
+ $qpt->set('recur_amount', $qpt->recur_amount + $tax_link->amount);
+ $qpt->set('setup_amount', sprintf('%.2f', $qpt->setup_amount - $tax_link->amount));
+ $qpt->set('setup_amount', 0) if $qpt->setup_amount < 0;
+ }
+ } elsif ($target->feepart) {
+ # do nothing; we already warned for the fee itself
+ } else {
+ # tax on tax: the tax target is another tax item
+ # since this is an estimate, I'm just going to assign it to the
+ # first of the underlying packages
+ my $sublinks = $target->cust_bill_pkg_tax_rate_location;
+ if ($sublinks and $sublinks->[0]) {
+ $tax_link->set('taxable_billpkgnum', $sublinks->[0]->taxable_billpkgnum);
+ push @$links, $tax_link; #try again
+ } else {
+ warn "$me unable to assign tax on tax; ignoring\n";
+ }
+ }
+ } # while my $tax_link
+ } # foreach my $cust_bill_pkg
+ #XXX discounts
+ }
+ foreach my $quotation_pkg (values %quotation_pkg) {
+ $error = $quotation_pkg->replace;
+ return "$error (recording estimate for ".$quotation_pkg->part_pkg->pkg.")"
+ if $error;
+ }
+ foreach my $quotation_pkg_tax (map { values %$_ } values %quotation_pkg_tax) {
+ $error = $quotation_pkg_tax->insert;
+ return "$error (recording estimated tax for ".$quotation_pkg_tax->itemdesc.")"
+ if $error;
+ }
+ return;