WFDASH-WEBAPP/src/lib/placeholder-customers.ts
SJCoetz 4444842ef5 feat: collection-map updates, todays-stops, quote/approval rework, buyer-tasks, e2e
- Add todays-stops test and component to collection-map
- Expand collection-map sidebar and pin-model
- Rework orders/[id]/process page
- Add quotes/[id]/page and process pages
- Add quotes/approvals/[id] page
- Add buyer-tasks generate-po route
- Add quotes partial-approve and pdf routes
- Add purchase-orders [id]/pdf route
- Add e2e create-flows spec
- Add superpowers plan doc
- Update pb_schema-2.json
- Update form components (company, customer, order, quote, supplier)
- Update UI components (button, command, dialog)
- Add collection-pool test components

Co-authored-by: Orca <help@stably.ai>
2026-08-03 09:33:45 +02:00

58 lines
2.3 KiB
TypeScript

/**
* Placeholder customer / company sentinels.
*
* n8n's email-intake pipeline creates these records when it can't
* match the sender's email to a real customer (or to a real
* company) — they're the "operator needs to fill this in" marker.
* The quote / order goes through the system with these IDs so the
* downstream flows (PDF gen, approval routing, email send) keep
* working, but the office is expected to swap them out for the
* real customer before the quote goes out.
*
* Centralised here so the picker UI (`CustomerSearch`), the PDF
* guard, and the service-layer validation all agree on what counts
* as "real". Adding a new placeholder is a one-line change instead
* of a three-file find-and-replace.
*
* Detection rules (in order):
* 1. The customer record's PB id is in `PLACEHOLDER_CUSTOMER_IDS`.
* Stable PKs, so this matches even if the row's `customer`
* name has been re-typed.
* 2. The customer name matches one of `PLACEHOLDER_CUSTOMER_NAMES`
* exactly (after trim). Defensive fallback in case the row
* was re-created after a PB wipe and the id changed.
* 3. The customer has no email. The n8n pipeline writes these
* sentinels with `email = ""` because it never matched them
* to a real contact; legitimate customers should always have
* an email on file. Catches the "MANUAL - SET CUSTOMER"
* placeholder even if a future operator renamed it.
*/
export const PLACEHOLDER_CUSTOMER_IDS = new Set<string>([
"2tmt34gzj44v9f8", // SELECT/CREATE CUSTOMER (process-page initial selection)
"dcpb38w4mk6vrm5", // Unknown Sender (email intake sentinel)
"xmd68ps32k1x5fu", // MANUAL - SET CUSTOMER (n8n email pipeline sentinel)
]);
export const PLACEHOLDER_CUSTOMER_NAMES = new Set<string>([
"SELECT/CREATE CUSTOMER",
"Unknown Sender",
"No Header",
"MANUAL - SET CUSTOMER",
]);
/**
* True iff the customer is one of the placeholder sentinels and
* the operator needs to swap it for a real one before pricing /
* sending the quote. Pass anythin with an `id`, `customer`, and
* `email` shape — we don't need the full record.
*/
export function isPlaceholderCustomer(
c: { id: string; customer?: string; email?: string },
): boolean {
return (
PLACEHOLDER_CUSTOMER_IDS.has(c.id) ||
PLACEHOLDER_CUSTOMER_NAMES.has((c.customer || "").trim()) ||
!c.email
);
}