init
This commit is contained in:
196
migrations/001_init.sql
Normal file
196
migrations/001_init.sql
Normal file
@@ -0,0 +1,196 @@
|
||||
-- BizMatch Phase 2 — initial schema
|
||||
-- Matches the agreed data model (Buyer / Contact / NDA / Deal / Business
|
||||
-- / Note / Todo / Document / ExtractionJob / Staff)
|
||||
|
||||
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- ---------------------------------------------------------------- staff
|
||||
CREATE TABLE staff (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name text NOT NULL UNIQUE,
|
||||
active boolean NOT NULL DEFAULT true,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------------- business
|
||||
CREATE TABLE business (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name text NOT NULL,
|
||||
nas_path text NOT NULL UNIQUE,
|
||||
status text NOT NULL CHECK (status IN ('ACTIVE', 'SOLD', 'INACTIVE')),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX business_status_idx ON business (status);
|
||||
CREATE TRIGGER business_updated BEFORE UPDATE ON business
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ---------------------------------------------------------------- buyer
|
||||
CREATE TABLE buyer (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_name text,
|
||||
status text NOT NULL DEFAULT 'ACTIVE'
|
||||
CHECK (status IN ('ACTIVE', 'DEACTIVATED', 'LEGACY')),
|
||||
legacy_json jsonb,
|
||||
created_by uuid REFERENCES staff (id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX buyer_status_idx ON buyer (status);
|
||||
CREATE TRIGGER buyer_updated BEFORE UPDATE ON buyer
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- -------------------------------------------------------------- contact
|
||||
CREATE TABLE contact (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
buyer_id uuid NOT NULL REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
email text,
|
||||
phone text,
|
||||
is_primary boolean NOT NULL DEFAULT false,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX contact_buyer_idx ON contact (buyer_id);
|
||||
-- Dedup anchor: normalized e-mail
|
||||
CREATE INDEX contact_email_idx ON contact (lower(btrim(email))) WHERE email IS NOT NULL;
|
||||
|
||||
-- ------------------------------------------------------------------ nda
|
||||
-- One "request round": for every new request the buyer fills in a new NDA.
|
||||
CREATE TABLE nda (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
buyer_id uuid NOT NULL REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
status text NOT NULL DEFAULT 'SENT'
|
||||
CHECK (status IN ('SENT', 'SIGNED')),
|
||||
nas_path text,
|
||||
sent_at date,
|
||||
signed_at date,
|
||||
preferred_businesses_text text,
|
||||
created_by uuid REFERENCES staff (id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX nda_buyer_idx ON nda (buyer_id);
|
||||
CREATE INDEX nda_open_idx ON nda (sent_at) WHERE status = 'SENT';
|
||||
CREATE TRIGGER nda_updated BEFORE UPDATE ON nda
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ----------------------------------------------------------------- deal
|
||||
-- Relation buyer <-> business within one NDA round.
|
||||
-- Deliberately NO unique constraint on (buyer_id, business_id):
|
||||
-- a new NDA round => a new deal, the history stays visible.
|
||||
CREATE TABLE deal (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
buyer_id uuid NOT NULL REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
business_id uuid NOT NULL REFERENCES business (id),
|
||||
nda_id uuid REFERENCES nda (id),
|
||||
status text NOT NULL DEFAULT 'NEW'
|
||||
CHECK (status IN ('NEW', 'INFO_SENT', 'DUE_DILIGENCE',
|
||||
'LOI', 'CLOSING', 'ENDED')),
|
||||
follow_up_at date,
|
||||
created_by uuid REFERENCES staff (id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX deal_buyer_idx ON deal (buyer_id);
|
||||
CREATE INDEX deal_business_idx ON deal (business_id);
|
||||
CREATE INDEX deal_status_idx ON deal (status);
|
||||
CREATE INDEX deal_follow_up_idx ON deal (follow_up_at)
|
||||
WHERE follow_up_at IS NOT NULL AND status <> 'ENDED';
|
||||
CREATE TRIGGER deal_updated BEFORE UPDATE ON deal
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ------------------------------------------------------------- document
|
||||
-- Pure NAS reference; the NAS stays the source of truth.
|
||||
CREATE TABLE document (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
nas_path text NOT NULL,
|
||||
kind text NOT NULL DEFAULT 'OTHER'
|
||||
CHECK (kind IN ('NDA', 'OVERVIEW', 'SUMMARY',
|
||||
'SELLER_AGREEMENT', 'LOI', 'OTHER')),
|
||||
buyer_id uuid REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
business_id uuid REFERENCES business (id) ON DELETE CASCADE,
|
||||
deal_id uuid REFERENCES deal (id) ON DELETE CASCADE,
|
||||
added_by uuid REFERENCES staff (id),
|
||||
added_at timestamptz NOT NULL DEFAULT now(),
|
||||
CONSTRAINT document_has_ref CHECK (
|
||||
num_nonnulls(buyer_id, business_id, deal_id) >= 1
|
||||
)
|
||||
);
|
||||
CREATE INDEX document_buyer_idx ON document (buyer_id) WHERE buyer_id IS NOT NULL;
|
||||
CREATE INDEX document_business_idx ON document (business_id) WHERE business_id IS NOT NULL;
|
||||
CREATE INDEX document_deal_idx ON document (deal_id) WHERE deal_id IS NOT NULL;
|
||||
|
||||
-- ----------------------------------------------------------------- note
|
||||
-- Attached to exactly ONE reference object (buyer, deal, business or NDA).
|
||||
CREATE TABLE note (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
text text NOT NULL,
|
||||
highlight boolean NOT NULL DEFAULT false,
|
||||
buyer_id uuid REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
deal_id uuid REFERENCES deal (id) ON DELETE CASCADE,
|
||||
business_id uuid REFERENCES business (id) ON DELETE CASCADE,
|
||||
nda_id uuid REFERENCES nda (id) ON DELETE CASCADE,
|
||||
created_by uuid NOT NULL REFERENCES staff (id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
CONSTRAINT note_exactly_one_ref CHECK (
|
||||
num_nonnulls(buyer_id, deal_id, business_id, nda_id) = 1
|
||||
)
|
||||
);
|
||||
CREATE INDEX note_buyer_idx ON note (buyer_id) WHERE buyer_id IS NOT NULL;
|
||||
CREATE INDEX note_deal_idx ON note (deal_id) WHERE deal_id IS NOT NULL;
|
||||
CREATE INDEX note_business_idx ON note (business_id) WHERE business_id IS NOT NULL;
|
||||
CREATE INDEX note_nda_idx ON note (nda_id) WHERE nda_id IS NOT NULL;
|
||||
|
||||
-- ----------------------------------------------------------------- todo
|
||||
-- kind TASK = normal task, REVIEW = request for proofreading/approval (with document_id).
|
||||
-- At most ONE case reference; no reference at all is allowed (general task).
|
||||
CREATE TABLE todo (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
text text NOT NULL,
|
||||
kind text NOT NULL DEFAULT 'TASK' CHECK (kind IN ('TASK', 'REVIEW')),
|
||||
assigned_to uuid NOT NULL REFERENCES staff (id),
|
||||
due_at date,
|
||||
status text NOT NULL DEFAULT 'OPEN' CHECK (status IN ('OPEN', 'DONE')),
|
||||
done_by uuid REFERENCES staff (id),
|
||||
done_at timestamptz,
|
||||
buyer_id uuid REFERENCES buyer (id) ON DELETE CASCADE,
|
||||
deal_id uuid REFERENCES deal (id) ON DELETE CASCADE,
|
||||
business_id uuid REFERENCES business (id) ON DELETE CASCADE,
|
||||
nda_id uuid REFERENCES nda (id) ON DELETE CASCADE,
|
||||
document_id uuid REFERENCES document (id) ON DELETE SET NULL,
|
||||
created_by uuid NOT NULL REFERENCES staff (id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
CONSTRAINT todo_max_one_ref CHECK (
|
||||
num_nonnulls(buyer_id, deal_id, business_id, nda_id) <= 1
|
||||
)
|
||||
);
|
||||
CREATE INDEX todo_assignee_idx ON todo (assigned_to, status);
|
||||
CREATE INDEX todo_due_idx ON todo (due_at) WHERE status = 'OPEN';
|
||||
CREATE TRIGGER todo_updated BEFORE UPDATE ON todo
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ------------------------------------------------------- extraction_job
|
||||
-- Queue for the asynchronous NDA extraction via vision_runner (worker polls QUEUED).
|
||||
CREATE TABLE extraction_job (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
nda_id uuid NOT NULL REFERENCES nda (id) ON DELETE CASCADE,
|
||||
status text NOT NULL DEFAULT 'QUEUED'
|
||||
CHECK (status IN ('QUEUED', 'RUNNING', 'DONE', 'FAILED')),
|
||||
attempts integer NOT NULL DEFAULT 0,
|
||||
result_json jsonb,
|
||||
error text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
started_at timestamptz,
|
||||
finished_at timestamptz,
|
||||
reviewed_by uuid REFERENCES staff (id),
|
||||
reviewed_at timestamptz
|
||||
);
|
||||
CREATE INDEX extraction_job_queue_idx ON extraction_job (created_at)
|
||||
WHERE status = 'QUEUED';
|
||||
CREATE INDEX extraction_job_nda_idx ON extraction_job (nda_id);
|
||||
Reference in New Issue
Block a user