-- ============================================================ -- Phase 1: Schema Expansion -- Neue Enums, Felder, Multi-Value Tabellen, Owner-Tabellen -- ============================================================ -- WICHTIG: ALTER TYPE ... ADD VALUE kann NICHT in einer Transaction laufen. -- Diese Migration muss daher ausserhalb von BEGIN/COMMIT ausgefuehrt werden, -- oder der ADD VALUE muss separat laufen. -- -------------------------------------------------------- -- 1. Neue Enums erstellen -- -------------------------------------------------------- CREATE TYPE "app_crm"."ContactSource" AS ENUM ( 'TRADE_FAIR', 'REFERRAL', 'WEBSITE', 'COLD_CALL', 'IMPORT', 'BUSINESS_CARD', 'OTHER' ); CREATE TYPE "app_crm"."EntityStatus" AS ENUM ( 'ACTIVE', 'INACTIVE', 'BLOCKED' ); CREATE TYPE "app_crm"."CompanySize" AS ENUM ( 'SIZE_1_10', 'SIZE_11_50', 'SIZE_51_200', 'SIZE_201_500', 'SIZE_500_PLUS' ); CREATE TYPE "app_crm"."OwnerRole" AS ENUM ( 'OWNER', 'MEMBER', 'WATCHER' ); CREATE TYPE "app_crm"."LostReason" AS ENUM ( 'PRICE', 'TIMING', 'COMPETITOR', 'NO_NEED', 'OTHER' ); CREATE TYPE "app_crm"."EmailType" AS ENUM ( 'WORK', 'PERSONAL', 'OTHER' ); CREATE TYPE "app_crm"."PhoneType" AS ENUM ( 'OFFICE', 'MOBILE', 'FAX' ); -- FOLLOWUP zum ActivityType Enum hinzufuegen (NICHT transaktionsfaehig!) ALTER TYPE "app_crm"."ActivityType" ADD VALUE IF NOT EXISTS 'FOLLOWUP'; -- -------------------------------------------------------- -- 2. Neue Spalten auf contacts -- -------------------------------------------------------- ALTER TABLE "app_crm"."contacts" ADD COLUMN "linkedin_url" VARCHAR(500), ADD COLUMN "birthday" TIMESTAMP(3), ADD COLUMN "source" "app_crm"."ContactSource", ADD COLUMN "department" VARCHAR(200), ADD COLUMN "status" "app_crm"."EntityStatus" NOT NULL DEFAULT 'ACTIVE'; -- -------------------------------------------------------- -- 3. Neue Spalten auf companies -- -------------------------------------------------------- ALTER TABLE "app_crm"."companies" ADD COLUMN "vat_id" VARCHAR(50), ADD COLUMN "tax_id" VARCHAR(50), ADD COLUMN "trade_register_number" VARCHAR(100), ADD COLUMN "register_court" VARCHAR(200), ADD COLUMN "company_size" "app_crm"."CompanySize", ADD COLUMN "delivery_street" VARCHAR(200), ADD COLUMN "delivery_zip" VARCHAR(20), ADD COLUMN "delivery_city" VARCHAR(100), ADD COLUMN "delivery_country" VARCHAR(2), ADD COLUMN "status" "app_crm"."EntityStatus" NOT NULL DEFAULT 'ACTIVE', ADD COLUMN "data_enriched_at" TIMESTAMP(3), ADD COLUMN "data_enriched_source" VARCHAR(200); -- -------------------------------------------------------- -- 4. Neue Spalten auf deals (Lost-Reason) -- -------------------------------------------------------- ALTER TABLE "app_crm"."deals" ADD COLUMN "lost_reason" "app_crm"."LostReason", ADD COLUMN "lost_reason_text" TEXT; -- -------------------------------------------------------- -- 5. Daten-Migration: isActive → status -- -------------------------------------------------------- UPDATE "app_crm"."contacts" SET "status" = 'INACTIVE' WHERE "is_active" = false; UPDATE "app_crm"."companies" SET "status" = 'INACTIVE' WHERE "is_active" = false; -- -------------------------------------------------------- -- 6. Multi-Value: contact_emails Tabelle -- -------------------------------------------------------- CREATE TABLE "app_crm"."contact_emails" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "contact_id" UUID, "company_id" UUID, "email" VARCHAR(255) NOT NULL, "type" "app_crm"."EmailType" NOT NULL DEFAULT 'WORK', "is_primary" BOOLEAN NOT NULL DEFAULT false, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "contact_emails_pkey" PRIMARY KEY ("id") ); CREATE INDEX "contact_emails_contact_id_idx" ON "app_crm"."contact_emails"("contact_id"); CREATE INDEX "contact_emails_company_id_idx" ON "app_crm"."contact_emails"("company_id"); ALTER TABLE "app_crm"."contact_emails" ADD CONSTRAINT "contact_emails_contact_id_fkey" FOREIGN KEY ("contact_id") REFERENCES "app_crm"."contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "app_crm"."contact_emails" ADD CONSTRAINT "contact_emails_company_id_fkey" FOREIGN KEY ("company_id") REFERENCES "app_crm"."companies"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- -------------------------------------------------------- -- 7. Multi-Value: contact_phones Tabelle -- -------------------------------------------------------- CREATE TABLE "app_crm"."contact_phones" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "contact_id" UUID, "company_id" UUID, "phone" VARCHAR(50) NOT NULL, "type" "app_crm"."PhoneType" NOT NULL DEFAULT 'OFFICE', "is_primary" BOOLEAN NOT NULL DEFAULT false, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "contact_phones_pkey" PRIMARY KEY ("id") ); CREATE INDEX "contact_phones_contact_id_idx" ON "app_crm"."contact_phones"("contact_id"); CREATE INDEX "contact_phones_company_id_idx" ON "app_crm"."contact_phones"("company_id"); ALTER TABLE "app_crm"."contact_phones" ADD CONSTRAINT "contact_phones_contact_id_fkey" FOREIGN KEY ("contact_id") REFERENCES "app_crm"."contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "app_crm"."contact_phones" ADD CONSTRAINT "contact_phones_company_id_fkey" FOREIGN KEY ("company_id") REFERENCES "app_crm"."companies"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- -------------------------------------------------------- -- 8. Daten-Migration: Bestehende Emails/Phones migrieren -- -------------------------------------------------------- -- Contact Emails INSERT INTO "app_crm"."contact_emails" ("id", "contact_id", "email", "type", "is_primary") SELECT gen_random_uuid(), "id", "email", 'WORK'::"app_crm"."EmailType", true FROM "app_crm"."contacts" WHERE "email" IS NOT NULL AND "email" != ''; -- Contact Phones (phone → OFFICE, mobile → MOBILE) INSERT INTO "app_crm"."contact_phones" ("id", "contact_id", "phone", "type", "is_primary") SELECT gen_random_uuid(), "id", "phone", 'OFFICE'::"app_crm"."PhoneType", true FROM "app_crm"."contacts" WHERE "phone" IS NOT NULL AND "phone" != ''; INSERT INTO "app_crm"."contact_phones" ("id", "contact_id", "phone", "type", "is_primary") SELECT gen_random_uuid(), "id", "mobile", 'MOBILE'::"app_crm"."PhoneType", false FROM "app_crm"."contacts" WHERE "mobile" IS NOT NULL AND "mobile" != ''; -- Company Emails INSERT INTO "app_crm"."contact_emails" ("id", "company_id", "email", "type", "is_primary") SELECT gen_random_uuid(), "id", "email", 'WORK'::"app_crm"."EmailType", true FROM "app_crm"."companies" WHERE "email" IS NOT NULL AND "email" != ''; -- Company Phones INSERT INTO "app_crm"."contact_phones" ("id", "company_id", "phone", "type", "is_primary") SELECT gen_random_uuid(), "id", "phone", 'OFFICE'::"app_crm"."PhoneType", true FROM "app_crm"."companies" WHERE "phone" IS NOT NULL AND "phone" != ''; -- Hinweis: Legacy-Spalten email, phone, mobile bleiben bestehen (deprecated) -- -------------------------------------------------------- -- 9. Owner-Tabellen: contact_owners -- -------------------------------------------------------- CREATE TABLE "app_crm"."contact_owners" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "tenant_id" UUID NOT NULL, "contact_id" UUID NOT NULL, "user_id" UUID NOT NULL, "role" "app_crm"."OwnerRole" NOT NULL DEFAULT 'OWNER', "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "contact_owners_pkey" PRIMARY KEY ("id") ); CREATE UNIQUE INDEX "contact_owners_contact_id_user_id_key" ON "app_crm"."contact_owners"("contact_id", "user_id"); CREATE INDEX "contact_owners_tenant_id_idx" ON "app_crm"."contact_owners"("tenant_id"); CREATE INDEX "contact_owners_tenant_id_contact_id_idx" ON "app_crm"."contact_owners"("tenant_id", "contact_id"); ALTER TABLE "app_crm"."contact_owners" ADD CONSTRAINT "contact_owners_contact_id_fkey" FOREIGN KEY ("contact_id") REFERENCES "app_crm"."contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- -------------------------------------------------------- -- 10. Owner-Tabellen: company_owners -- -------------------------------------------------------- CREATE TABLE "app_crm"."company_owners" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "tenant_id" UUID NOT NULL, "company_id" UUID NOT NULL, "user_id" UUID NOT NULL, "role" "app_crm"."OwnerRole" NOT NULL DEFAULT 'OWNER', "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "company_owners_pkey" PRIMARY KEY ("id") ); CREATE UNIQUE INDEX "company_owners_company_id_user_id_key" ON "app_crm"."company_owners"("company_id", "user_id"); CREATE INDEX "company_owners_tenant_id_idx" ON "app_crm"."company_owners"("tenant_id"); CREATE INDEX "company_owners_tenant_id_company_id_idx" ON "app_crm"."company_owners"("tenant_id", "company_id"); ALTER TABLE "app_crm"."company_owners" ADD CONSTRAINT "company_owners_company_id_fkey" FOREIGN KEY ("company_id") REFERENCES "app_crm"."companies"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- Bestehende Company.ownerId → company_owners migrieren INSERT INTO "app_crm"."company_owners" ("id", "tenant_id", "company_id", "user_id", "role") SELECT gen_random_uuid(), "tenant_id", "id", "owner_id", 'OWNER'::"app_crm"."OwnerRole" FROM "app_crm"."companies" WHERE "owner_id" IS NOT NULL; -- -------------------------------------------------------- -- 11. Owner-Tabellen: deal_owners -- -------------------------------------------------------- CREATE TABLE "app_crm"."deal_owners" ( "id" UUID NOT NULL DEFAULT gen_random_uuid(), "tenant_id" UUID NOT NULL, "deal_id" UUID NOT NULL, "user_id" UUID NOT NULL, "role" "app_crm"."OwnerRole" NOT NULL DEFAULT 'OWNER', "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "deal_owners_pkey" PRIMARY KEY ("id") ); CREATE UNIQUE INDEX "deal_owners_deal_id_user_id_key" ON "app_crm"."deal_owners"("deal_id", "user_id"); CREATE INDEX "deal_owners_tenant_id_idx" ON "app_crm"."deal_owners"("tenant_id"); CREATE INDEX "deal_owners_tenant_id_deal_id_idx" ON "app_crm"."deal_owners"("tenant_id", "deal_id"); ALTER TABLE "app_crm"."deal_owners" ADD CONSTRAINT "deal_owners_deal_id_fkey" FOREIGN KEY ("deal_id") REFERENCES "app_crm"."deals"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- -------------------------------------------------------- -- 12. Zusaetzliche Indexes fuer status -- -------------------------------------------------------- CREATE INDEX "contacts_tenant_id_status_idx" ON "app_crm"."contacts"("tenant_id", "status"); CREATE INDEX "companies_tenant_id_status_idx" ON "app_crm"."companies"("tenant_id", "status");