mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
Merge pull request #28 from ArnasDon/fix/contact-deletion-fk-constraints
fix(contacts): allow deletion — SET NULL on broadcast_recipients + deals FKs
This commit is contained in:
commit
6bae86bd66
@ -79,7 +79,9 @@ export function DealForm({
|
|||||||
setTitle(deal.title);
|
setTitle(deal.title);
|
||||||
setValue(String(deal.value ?? ""));
|
setValue(String(deal.value ?? ""));
|
||||||
setCurrency(deal.currency || "USD");
|
setCurrency(deal.currency || "USD");
|
||||||
setContactId(deal.contact_id);
|
// contact_id is nullable when the contact has been deleted
|
||||||
|
// (migration 004: ON DELETE SET NULL). "" means "no selection".
|
||||||
|
setContactId(deal.contact_id ?? "");
|
||||||
setStageId(deal.stage_id);
|
setStageId(deal.stage_id);
|
||||||
setAssignedTo(deal.assigned_to ?? "");
|
setAssignedTo(deal.assigned_to ?? "");
|
||||||
setExpectedCloseDate(deal.expected_close_date ?? "");
|
setExpectedCloseDate(deal.expected_close_date ?? "");
|
||||||
|
|||||||
@ -141,7 +141,11 @@ export interface Deal {
|
|||||||
user_id: string;
|
user_id: string;
|
||||||
pipeline_id: string;
|
pipeline_id: string;
|
||||||
stage_id: string;
|
stage_id: string;
|
||||||
contact_id: string;
|
/**
|
||||||
|
* Nullable after migration 004 — becomes NULL when the referenced
|
||||||
|
* contact is deleted (ON DELETE SET NULL). History preserved.
|
||||||
|
*/
|
||||||
|
contact_id: string | null;
|
||||||
conversation_id?: string;
|
conversation_id?: string;
|
||||||
assigned_to?: string;
|
assigned_to?: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -182,7 +186,12 @@ export interface Broadcast {
|
|||||||
export interface BroadcastRecipient {
|
export interface BroadcastRecipient {
|
||||||
id: string;
|
id: string;
|
||||||
broadcast_id: string;
|
broadcast_id: string;
|
||||||
contact_id: string;
|
/**
|
||||||
|
* Nullable after migration 004 — becomes NULL when the referenced
|
||||||
|
* contact is deleted (ON DELETE SET NULL). History preserved; the
|
||||||
|
* UI renders "Unknown" for orphaned rows.
|
||||||
|
*/
|
||||||
|
contact_id: string | null;
|
||||||
status: RecipientStatus;
|
status: RecipientStatus;
|
||||||
sent_at?: string;
|
sent_at?: string;
|
||||||
delivered_at?: string;
|
delivered_at?: string;
|
||||||
|
|||||||
65
supabase/migrations/004_contact_delete_set_null.sql
Normal file
65
supabase/migrations/004_contact_delete_set_null.sql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- Allow contact deletion without wiping history.
|
||||||
|
--
|
||||||
|
-- broadcast_recipients.contact_id and deals.contact_id were declared
|
||||||
|
-- NOT NULL REFERENCES contacts(id) with no ON DELETE action, so
|
||||||
|
-- Postgres defaults to NO ACTION. The first time a user tried to
|
||||||
|
-- delete a contact that had ever received a broadcast or been
|
||||||
|
-- attached to a deal, the delete failed with:
|
||||||
|
--
|
||||||
|
-- ERROR 23503: update or delete on table "contacts" violates
|
||||||
|
-- foreign key constraint ... on table <other>
|
||||||
|
--
|
||||||
|
-- CASCADE is the wrong fix — it would silently wipe historical
|
||||||
|
-- broadcast recipient rows (breaking audit + retroactively moving
|
||||||
|
-- broadcasts.sent_count / delivered_count / read_count etc. via the
|
||||||
|
-- aggregate trigger) and deal rows.
|
||||||
|
--
|
||||||
|
-- SET NULL is the right fix: history rows survive with a NULL
|
||||||
|
-- contact_id. The UI is already null-safe (contact?.name ?? 'Unknown',
|
||||||
|
-- contact?.phone, etc.).
|
||||||
|
--
|
||||||
|
-- Idempotent — safe to run multiple times.
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- ── broadcast_recipients.contact_id ────────────────────────────
|
||||||
|
ALTER TABLE broadcast_recipients
|
||||||
|
ALTER COLUMN contact_id DROP NOT NULL;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'broadcast_recipients_contact_id_fkey'
|
||||||
|
AND conrelid = 'broadcast_recipients'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE broadcast_recipients
|
||||||
|
DROP CONSTRAINT broadcast_recipients_contact_id_fkey;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
ALTER TABLE broadcast_recipients
|
||||||
|
ADD CONSTRAINT broadcast_recipients_contact_id_fkey
|
||||||
|
FOREIGN KEY (contact_id) REFERENCES contacts(id)
|
||||||
|
ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- ── deals.contact_id ───────────────────────────────────────────
|
||||||
|
ALTER TABLE deals
|
||||||
|
ALTER COLUMN contact_id DROP NOT NULL;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'deals_contact_id_fkey'
|
||||||
|
AND conrelid = 'deals'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE deals
|
||||||
|
DROP CONSTRAINT deals_contact_id_fkey;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
ALTER TABLE deals
|
||||||
|
ADD CONSTRAINT deals_contact_id_fkey
|
||||||
|
FOREIGN KEY (contact_id) REFERENCES contacts(id)
|
||||||
|
ON DELETE SET NULL;
|
||||||
Loading…
Reference in New Issue
Block a user