Closes#866.
Three features, all behind the `incomingInvoices` feature flag:
1. Attach the stored supplier proof PDF to the client-invoice email when a
captured invoice is re-billed/passed through, as a SEPARATE attachment so
invoice immutability holds. Global default (off), per-customer tri-state
override, and per-file selection in a new Send dialog. A missing proof at
issue time stamps inbound_documents.proof_attach_error rather than silently
dropping, and never blocks the send. Proof filename is a configurable
template with {INVOICE} {SUPPLIER} {YEAR} {MONTH} {SEQ}/{SEQ:0Nd} tokens.
2. Re-bills & passthrough panel under CRM → Customer, grouped Open/Sent/Paid
with status derived from the linked invoice lifecycle rather than a
duplicated column.
3. Cross-add dialog rolling open hours and open re-bills into one invoice,
symmetric from both entry points. The two stay distinct, contiguous line
groups — never merged into shared line items.
Migration 169 is additive, hasColumn-guarded and idempotent.
Review (two rounds) closed two concerns:
- Storno stranding: nothing cleared inbound_documents.billed_invoice_id when a
covering invoice was cancelled, so a Storno'd re-bill showed as Open in the
new panel while every billing path filters on that column being NULL — the
supplier cost could never be re-billed. releaseRebillsForCancelledInvoice now
detaches the linkage on both invoice-cancel paths, with a regression test on
the issued-cancel path.
- Permission gating: the new controls rendered on data presence alone while
their endpoints require accounting.view / accounting.manage / customers.edit.
Now gated at both the query and render layers.
Known follow-up: two cross-add counter queries are gated on a permission their
endpoint does not check (HoursSection.tsx:174, CustomerCrmPanels.tsx:270) —
degrades safely, one line each.