---
name: evolution_invoice_billto_vs_project_owner
description: Invoice bill-to (clientid) may differ from the project owner; link is jobid-only. Option-3 policy shipped 2026-07-16; historical backfill is customer-driven only.
metadata: 
  node_type: memory
  type: project
  originSessionId: 299ece65-d740-4e61-800c-01874582e39b
---

An invoice's **bill-to** (`invoices.clientid`) and the **project it belongs to** (`jobid`)
are independent dimensions. The job↔invoice link is by `jobid` ONLY — clientid plays no part.
This deliberately allows an invoice to bill to a different customer than the job's own client
(joint ventures, SPV/project billing entities). Shipped 2026-07-16.

**Why:** original bug — changing the invoice's customer silently reset the Job dropdown to
"Not Applicable" (JS `loadDefaults()` rebuilt `#jobList` from the new client's jobs only),
orphaning the invoice from the job with no warning. Job/project reports key on `jobid`, AR
keys on `clientid`, so an orphan/divergence was invisible on one side. Chose **Option 3**
(allow divergence, made visible) over hard blocks.

**How to apply / what exists now:**
- `invoiceadd.js :: loadDefaults()` preserves the current job across a bill-to change
  (re-inserts it flagged `data-offaccount="1"`, keeps it selected). Never re-introduce the
  silent reset.
- `invoiceadd.inc` renders a divergence notice by the Job selector; `updateJobClientMismatch()`
  toggles it live. Committed invoices lock the client/job pickers (readonly doesn't work on
  `<select>` — a mousedown/keydown guard does).
- `job.php :: getInvoices()` adds a "Billed to: …" badge on rows where invoice.clientid ≠ job client.
- `invoicejob.inc` target-invoice picker keys off `(clientid = ? OR jobid = ?)`.
- `jobeditgen.inc :: allowChangeClient()` is now a confirm() warning, not the old hard block.
- Committed → Xero re-syncs under the new bill-to on re-commit (`library/xero.php::putInvoice`).

**Historical data: customer-driven only.** Leave existing data as is; do NOT run a proactive
backfill/detection sweep. Only build repair (`jobid=0` orphans, `clientid≠job.clientid`) if a
customer reports a discrepancy or asks. Living doc: `evolution/audits/INVOICE_JOB_CLIENT_DIVERGENCE.md`.

Related: [[evolution_invoiceitems_jobid_source_of_truth]], [[evolution_invoice_staffid_masterid]],
[[evolution_invoice_total_reconciliation]].
