Foundation for WhatsApp receipt intake (plan 2026-08-01), no runtime callers yet: - whatsapp_phone_links + whatsapp_link_codes: verified phone -> user binding via single-use 10-min codes (invite-token pattern). Peppered HMAC lookup hash + AES-GCM encrypted number; one ACTIVE link per phone and per user (partial unique, revocation preserves history). User-scoped RLS (a binding belongs to a person, not a company). - whatsapp_conversations + whatsapp_messages: deterministic state machine state and the message log, which doubles as the durable job record for persist-first webhook processing. Partial unique index on inbound wamid = the at-least-once dedupe key. Service-role only. - whatsapp_sender_rate_counters + check_and_increment_whatsapp_sender_quota: the pre-binding limiter keyed by phone hash; EXECUTE granted to service_role only. - invoice_inbox_items: source CHECK widened to 'whatsapp', plus whatsapp_message_id (one item per delivering message) and channel_context jsonb, kept separate from extracted_data so verified human answers never share a container with untrusted OCR output. document_attachments.upload_source CHECK gains 'whatsapp'. - whatsapp_conversations triaged into ARCHIVE_EXCLUDED_TABLES (full-archive coverage contract). pg-real on a fresh DB: 975/975 incl. the new whatsapp-channel suite (RLS visibility, unique/rebinding semantics, wamid dedupe, CHECK widenings, quota RPC caps + grant lockdown). Co-authored-by: Jakob Wennberg <jakob.wennberg@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
44 lines
2.1 KiB
SQL
44 lines
2.1 KiB
SQL
-- WhatsApp channel, part 3/3: widen the inbox to the new source.
|
|
--
|
|
-- invoice_inbox_items.source and document_attachments.upload_source were
|
|
-- created as inline column CHECKs, so their constraint names are the
|
|
-- Postgres auto-generated <table>_<column>_check (verified: no later
|
|
-- migration re-created either). NOT VALID -> VALIDATE keeps the ADD cheap
|
|
-- on the large tables; the pg-real suite (whatsapp-tables.pg.test.ts)
|
|
-- asserts a 'whatsapp' insert succeeds, which fails loudly if the drop
|
|
-- missed a differently-named constraint and the old CHECK survived.
|
|
|
|
ALTER TABLE public.invoice_inbox_items
|
|
DROP CONSTRAINT IF EXISTS invoice_inbox_items_source_check;
|
|
ALTER TABLE public.invoice_inbox_items
|
|
ADD CONSTRAINT invoice_inbox_items_source_check
|
|
CHECK (source IN ('email','upload','whatsapp')) NOT VALID;
|
|
ALTER TABLE public.invoice_inbox_items
|
|
VALIDATE CONSTRAINT invoice_inbox_items_source_check;
|
|
|
|
-- Provenance link back to the chat message that delivered the file
|
|
-- (quoted-reply resolution + idempotency at the item level), and the
|
|
-- chat-context container for clarifying-question answers. channel_context
|
|
-- is deliberately SEPARATE from extracted_data: retry-extraction
|
|
-- overwrites extracted_data wholesale, and verified human answers must
|
|
-- never share a container with untrusted OCR output.
|
|
ALTER TABLE public.invoice_inbox_items
|
|
ADD COLUMN IF NOT EXISTS whatsapp_message_id uuid REFERENCES public.whatsapp_messages(id) ON DELETE SET NULL,
|
|
ADD COLUMN IF NOT EXISTS channel_context jsonb;
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS invoice_inbox_items_whatsapp_msg
|
|
ON public.invoice_inbox_items (whatsapp_message_id)
|
|
WHERE whatsapp_message_id IS NOT NULL;
|
|
|
|
ALTER TABLE public.document_attachments
|
|
DROP CONSTRAINT IF EXISTS document_attachments_upload_source_check;
|
|
ALTER TABLE public.document_attachments
|
|
ADD CONSTRAINT document_attachments_upload_source_check
|
|
CHECK (upload_source IN (
|
|
'camera', 'file_upload', 'email', 'e_invoice', 'scan', 'api', 'system', 'whatsapp'
|
|
)) NOT VALID;
|
|
ALTER TABLE public.document_attachments
|
|
VALIDATE CONSTRAINT document_attachments_upload_source_check;
|
|
|
|
NOTIFY pgrst, 'reload schema';
|