OUT-3840: bill company invoices to earliest active client when company name is off - #57
Conversation
…ompany name is off When useCompanyName is off and an invoice is billed to a company, the sync previously threw "Cannot use company name of placeholder company". Fall back to the earliest active client of the company instead, and add getEarliestActiveClient to select it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Greptile SummaryThis PR fixes a regression where
Confidence Score: 5/5Safe to merge — the three billing branches are logically sound, DB query conditions derive consistently from the resolved state, and the getEarliestActiveClient helper handles all edge cases including empty lists and timezone-aware timestamps. The billing-target resolution was cleanly restructured into three explicit, non-overlapping branches. The billedClientId rename carries the resolved identity correctly through every DB read, delete, and log call. The getCompanyClients call uses the pre-existing limit: 10000 pattern. No data path produces wrong state or silently drops errors. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getSyncedContact\nclientId?, companyId] --> B{clientId\npresent?}
B -- No --> C{useCompanyName?}
C -- off --> D[getCompanyClients companyId\ngetEarliestActiveClient]
D --> E{active client\nfound?}
E -- No --> F[throw: No active client\nfor company 400]
E -- Yes --> G[billedClientId = client.id\nuserType = CLIENT]
C -- on --> H[getCompany companyId]
H --> I{isPlaceholder?}
I -- Yes --> J[throw: Cannot use company name\nof placeholder 400]
I -- No --> K[useNonPlaceholderCompanyName = true\nuserType = COMPANY]
B -- Yes --> L[billedClientId = clientId\nuserType = CLIENT]
G --> M[Query syncedContacts\nby billedClientId / CLIENT]
K --> N[Query syncedContacts\nby companyId / COMPANY]
L --> O[Query syncedContacts\nby clientId / CLIENT]
M & N & O --> P{Row found &\nXero contact valid?}
P -- Yes --> Q[validateXeroContact\nreturn contact]
P -- No --> R[Delete stale row\ncreateContact]
Reviews (2): Last reviewed commit: "refactor(OUT-3840): address PR review fe..." | Re-trigger Greptile |
- Compare client createdAt as Date objects instead of lexically - Drop redundant data object that duplicated the APIError message Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Fixes OUT-3840. When Use company name is off and an invoice is billed to a company (no
clientId), the contact sync previously threwCannot use company name of placeholder company for invoicefor every such invoice — even non-placeholder companies. It now falls back to billing the earliest active client of the company.Changes
getEarliestActiveClient(src/lib/copilot/utils.ts) — filters clients tostatus === 'active'and picks the earliest bycreatedAt.SyncedContactsService#getSyncedContact:No active client found for companyif none)tempClientId→billedClientId.Billing matrix
Notes
statusvalues arenotInvited/invited/active; onlyactiveis selected.main: flag-on invoices billed to an individual client now sync as the client contact (previously the company). Existing customers synced as company contacts under the old logic may get a new client contact created in Xero — no backfill.Verification
pnpm typecheck✅pnpm lint✅🤖 Generated with Claude Code