Skip to content

fix(qbo): pin TxnTaxCodeRef from the estimate's jurisdiction on milestone pushes - #432

Open
Clarion1631 wants to merge 3 commits into
fix/qbo-milestone-ship-addrfrom
fix/qbo-milestone-tax-code
Open

fix(qbo): pin TxnTaxCodeRef from the estimate's jurisdiction on milestone pushes#432
Clarion1631 wants to merge 3 commits into
fix/qbo-milestone-ship-addrfrom
fix/qbo-milestone-tax-code

Conversation

@Clarion1631

Copy link
Copy Markdown
Owner

Why

QBO milestone invoices carried TxnTaxDetail.TotalTax but no TxnTaxCodeRef, so QuickBooks applied its default tax code and recomputed tax at that code's rate. Berg ADU INV-00177-2 (Winlock, 8.0%) was created at WA-Vancouver 8.9%: $15,125 on a $15,000 milestone, a phantom $125 balance after the customer paid by check. (Data already corrected in QBO on 2026-08-29.)

What

  • resolveQBTaxCodeId(tokens, taxRateName): SELECT ... FROM TaxCode WHERE Name = '<Estimate.taxRateName>', active + case-insensitive match; returns null with a console warning when unset, unmatched, or the lookup errors (fail-soft, billing never blocks).
  • createQBMilestoneInvoice: new optional taxCodeIdTxnTaxDetail.TxnTaxCodeRef; only on taxed invoices; payload byte-identical to before when no code resolves.
  • Both push sites (pushMilestoneToQuickBooks, stageProgressBillingToQuickBooksCore) select estimate.taxRateName and pass the code.
  • tests/qbo-tax-code-pin.test.ts (registered in test:unit): 7 cases covering matching, fallbacks, escaping, and the three payload shapes.

Verification

  • npx tsx --test tests/qbo-tax-code-pin.test.ts: 7/7 pass
  • npx tsc --noEmit: clean
  • Codex review (gpt-5.6-sol): round 1 asked for test coverage; added.

Notes

🤖 Generated with Claude Code

https://claude.ai/code/session_013nETMsFif1rbvPijc6BgQN

…tone pushes

QBO milestone invoices were sent with a pre-tax line + TxnTaxDetail.TotalTax but no
TxnTaxCodeRef, so QuickBooks applied the customer/company default tax code and
recomputed the tax at THAT code's rate. Berg ADU INV-00177-2 (Winlock, 8.0%) came
out at WA-Vancouver 8.9%: $15,125 against a $15,000 milestone, leaving a phantom
$125 balance after the customer's check.

- resolveQBTaxCodeId(): map Estimate.taxRateName ("Winlock") to the active QBO
  TaxCode with the same name; null (and a warning) when unset/unmatched/lookup
  fails, so billing never blocks on the lookup.
- createQBMilestoneInvoice(): optional taxCodeId -> TxnTaxDetail.TxnTaxCodeRef,
  emitted only on taxed invoices; payload unchanged when no code resolves.
- pushMilestoneToQuickBooks / stageProgressBillingToQuickBooksCore: select the
  estimate's taxRateName and pass the resolved code.
- tests/qbo-tax-code-pin.test.ts: name matching, inactive/no-match/error
  fallbacks, quote escaping, and the three payload shapes.

Stacked on fix/qbo-milestone-ship-addr (#431) since both edit the same push sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nETMsFif1rbvPijc6BgQN
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
probuild Ready Ready Preview Aug 31, 2026 4:17am

Request Review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nETMsFif1rbvPijc6BgQN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant