From 6b90bab30c6a01b87ebacfb4f4cbc929cc34af62 Mon Sep 17 00:00:00 2001 From: Jackson Roberts Date: Wed, 8 Apr 2026 13:40:29 +0000 Subject: [PATCH] fix(api): add catch-up migration to sync db-push schema drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All schema changes that were applied via 'prisma db push' over the past several months were never captured in migration files. When the postgres pod restarted just before the migration job ran, the database was rebuilt from the 15 existing migrations -- creating an old schema that was missing ~20 tables and significant structural changes to User, Opportunity, CatalogItem, and Company. This migration bridges the gap idempotently: - New enums: PhoneType, FaxType, BillingMethod, BillingType, GenderType, USState, Country, OpportunityInterest - User: add firstName/lastName/title/active/hidden/cwMemberId/updatedBy; drop emailVerified/name; make userId nullable - CatalogItem: TEXT id → INTEGER id + TEXT uid PK; restructure FK columns - Company: TEXT id → INTEGER id + TEXT uid PK; drop old CW columns; add dateDeleted/deleteFlag/phone/taxExempt/taxId/website/enteredById - Opportunity: TEXT id → INTEGER id + TEXT uid PK; drop ~25 flat CW columns; add typeId/statusId/contactId/siteId/locationId/departmentId/ closedById/primarySalesRepId/secondarySalesRepId/eneteredBy/updatedBy/ oppNarrative/taxCodeId/interest; drop cwDateEntered - UnifiSite: companyId TEXT → INTEGER - 20+ new tables: CorporateLocation, InternalDepartment, CompanyAddress, Contact, CatalogItemType, CatalogCategory, CatalogSubcategory, CatalogManufacturer, Warehouse, WarehouseBin, ProductInventory, MinimumStockByWarehouse, ProductData, ServiceTicket, ServiceTicketNote, ServiceTicketType, ServiceTicketBoard, ServiceTicketLocation, ServiceTicketSource, ServiceTicketImpact, ServiceTicketPriority, ServiceTicketServerity, ServiceTicketFinalData, OpportunityType, OpportunityStatus, ScheduleStatus, ScheduleType, ScheduleSpan, Schedule, TaxCode Verified: all 16 migrations apply cleanly on a fresh DB and produce zero schema drift (prisma migrate diff outputs '-- This is an empty migration.') Fixes P2022 ColumnNotFound errors on login and all model queries. --- .../migration.sql | 1497 +++++++++++++++++ 1 file changed, 1497 insertions(+) create mode 100644 api/prisma/migrations/20260408100000_sync_schema_from_db_push/migration.sql diff --git a/api/prisma/migrations/20260408100000_sync_schema_from_db_push/migration.sql b/api/prisma/migrations/20260408100000_sync_schema_from_db_push/migration.sql new file mode 100644 index 0000000..4468b0c --- /dev/null +++ b/api/prisma/migrations/20260408100000_sync_schema_from_db_push/migration.sql @@ -0,0 +1,1497 @@ +-- ============================================================================= +-- Comprehensive schema sync: captures all schema evolution that happened via +-- `prisma db push` and was never written into a proper migration file. +-- +-- Generated by diffing the migration-created schema against the current +-- schema.prisma using `prisma migrate diff`. +-- +-- All operations are idempotent (IF NOT EXISTS / IF EXISTS / DO $$ ... $$) +-- so this migration is safe to run against both: +-- a) A fresh DB created by the prior 15 migrations (current prod state) +-- b) A DB that already had the db-push evolved schema applied +-- ============================================================================= + +-- ============================================================================= +-- SECTION 1: New ENUM types +-- ============================================================================= + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'PhoneType') THEN + CREATE TYPE "PhoneType" AS ENUM ('DIRECT', 'MOBILE', 'HOME', 'COMPANY', 'SITE'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'FaxType') THEN + CREATE TYPE "FaxType" AS ENUM ('FAX', 'COMPANY', 'SITE'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'BillingMethod') THEN + CREATE TYPE "BillingMethod" AS ENUM ('ACTUAL_RATES', 'FIXED_FEE', 'NOT_TO_EXCEED', 'OVERRIDE_RATE'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'BillingType') THEN + CREATE TYPE "BillingType" AS ENUM ('STANDARD', 'PROJECT'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'GenderType') THEN + CREATE TYPE "GenderType" AS ENUM ('MALE', 'FEMALE'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'USState') THEN + CREATE TYPE "USState" AS ENUM ( + 'AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA','HI','ID','IL','IN', + 'IA','KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV', + 'NH','NJ','NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN', + 'TX','UT','VT','VA','WA','WV','WI','WY' + ); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'Country') THEN + CREATE TYPE "Country" AS ENUM ('US'); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'OpportunityInterest') THEN + CREATE TYPE "OpportunityInterest" AS ENUM ('HOT', 'WARM', 'COLD'); + END IF; +END $$; + +-- ============================================================================= +-- SECTION 2: User table — evolve from initial flat schema to current model +-- ============================================================================= + +DO $$ BEGIN + -- Remove columns no longer in schema + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'emailVerified') THEN + ALTER TABLE "User" DROP COLUMN "emailVerified"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'name') THEN + ALTER TABLE "User" DROP COLUMN "name"; + END IF; + + -- Add new columns + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'firstName') THEN + ALTER TABLE "User" ADD COLUMN "firstName" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'lastName') THEN + ALTER TABLE "User" ADD COLUMN "lastName" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'title') THEN + ALTER TABLE "User" ADD COLUMN "title" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'active') THEN + ALTER TABLE "User" ADD COLUMN "active" BOOLEAN NOT NULL DEFAULT true; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'hidden') THEN + ALTER TABLE "User" ADD COLUMN "hidden" BOOLEAN NOT NULL DEFAULT false; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'cwMemberId') THEN + ALTER TABLE "User" ADD COLUMN "cwMemberId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'User' AND column_name = 'updatedBy') THEN + ALTER TABLE "User" ADD COLUMN "updatedBy" TEXT; + END IF; + + -- Make userId nullable (was NOT NULL in initial migration) + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'User' AND column_name = 'userId' AND is_nullable = 'NO' + ) THEN + ALTER TABLE "User" ALTER COLUMN "userId" DROP NOT NULL; + END IF; +END $$; + +-- Unique indexes for new User columns (idempotent) +CREATE UNIQUE INDEX IF NOT EXISTS "User_cwIdentifier_key" ON "User"("cwIdentifier"); +CREATE UNIQUE INDEX IF NOT EXISTS "User_cwMemberId_key" ON "User"("cwMemberId"); + +-- FK: User.cwMemberId -> CwMember.cwMemberId +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE constraint_name = 'User_cwMemberId_fkey' + ) THEN + ALTER TABLE "User" ADD CONSTRAINT "User_cwMemberId_fkey" + FOREIGN KEY ("cwMemberId") REFERENCES "CwMember"("cwMemberId") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +-- ============================================================================= +-- SECTION 3: CatalogItem — change id TEXT→INTEGER, add uid PK, restructure +-- ============================================================================= + +-- Drop FKs that reference CatalogItem by old id column +DO $$ BEGIN + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = '_LinkedItems_A_fkey') THEN + ALTER TABLE "_LinkedItems" DROP CONSTRAINT "_LinkedItems_A_fkey"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = '_LinkedItems_B_fkey') THEN + ALTER TABLE "_LinkedItems" DROP CONSTRAINT "_LinkedItems_B_fkey"; + END IF; +END $$; + +-- Drop old unique index that no longer applies +DROP INDEX IF EXISTS "CatalogItem_cwCatalogId_key"; + +-- Restructure CatalogItem +DO $$ BEGIN + -- Add uid PK column if missing + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'uid') THEN + ALTER TABLE "CatalogItem" ADD COLUMN "uid" TEXT NOT NULL DEFAULT ''; + END IF; + + -- Swap PK from id to uid (only if id is still the primary key) + IF EXISTS ( + SELECT 1 FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name + WHERE tc.table_name = 'CatalogItem' AND tc.constraint_type = 'PRIMARY KEY' AND kcu.column_name = 'id' + ) THEN + ALTER TABLE "CatalogItem" DROP CONSTRAINT "CatalogItem_pkey"; + ALTER TABLE "CatalogItem" ADD CONSTRAINT "CatalogItem_pkey" PRIMARY KEY ("uid"); + END IF; + + -- Change id from TEXT to INTEGER (only if still TEXT) + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'CatalogItem' AND column_name = 'id' AND data_type = 'text' + ) THEN + ALTER TABLE "CatalogItem" DROP COLUMN "id"; + ALTER TABLE "CatalogItem" ADD COLUMN "id" INTEGER; + END IF; + + -- Drop old columns no longer in schema + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'cwCatalogId') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "cwCatalogId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'category') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "category"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'categoryCwId') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "categoryCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'manufacturer') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "manufacturer"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'manufactureCwId') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "manufactureCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'subcategory') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "subcategory"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'subcategoryCwId') THEN + ALTER TABLE "CatalogItem" DROP COLUMN "subcategoryCwId"; + END IF; + + -- Add new columns + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'classId') THEN + ALTER TABLE "CatalogItem" ADD COLUMN "classId" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'manufacturerId') THEN + ALTER TABLE "CatalogItem" ADD COLUMN "manufacturerId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'CatalogItem' AND column_name = 'subcategoryId') THEN + ALTER TABLE "CatalogItem" ADD COLUMN "subcategoryId" INTEGER NOT NULL DEFAULT 0; + END IF; +END $$; + +CREATE UNIQUE INDEX IF NOT EXISTS "CatalogItem_id_key" ON "CatalogItem"("id"); + +-- ============================================================================= +-- SECTION 4: Company — change id TEXT→INTEGER, add uid PK, add columns +-- ============================================================================= + +-- Drop FKs that reference Company by old id +DO $$ BEGIN + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Credential_companyId_fkey') THEN + ALTER TABLE "Credential" DROP CONSTRAINT "Credential_companyId_fkey"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'UnifiSite_companyId_fkey') THEN + ALTER TABLE "UnifiSite" DROP CONSTRAINT "UnifiSite_companyId_fkey"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_companyId_fkey') THEN + ALTER TABLE "Opportunity" DROP CONSTRAINT "Opportunity_companyId_fkey"; + END IF; +END $$; + +DROP INDEX IF EXISTS "Company_cw_CompanyId_key"; +DROP INDEX IF EXISTS "Company_cw_Identifier_key"; + +DO $$ BEGIN + -- Add uid PK column if missing + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'uid') THEN + ALTER TABLE "Company" ADD COLUMN "uid" TEXT NOT NULL DEFAULT ''; + END IF; + + -- Swap PK from id to uid + IF EXISTS ( + SELECT 1 FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name + WHERE tc.table_name = 'Company' AND tc.constraint_type = 'PRIMARY KEY' AND kcu.column_name = 'id' + ) THEN + ALTER TABLE "Company" DROP CONSTRAINT "Company_pkey"; + ALTER TABLE "Company" ADD CONSTRAINT "Company_pkey" PRIMARY KEY ("uid"); + END IF; + + -- Change id from TEXT to INTEGER + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'Company' AND column_name = 'id' AND data_type = 'text' + ) THEN + ALTER TABLE "Company" DROP COLUMN "id"; + ALTER TABLE "Company" ADD COLUMN "id" INTEGER; + END IF; + + -- Drop old CW-specific columns + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'cw_CompanyId') THEN + ALTER TABLE "Company" DROP COLUMN "cw_CompanyId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'cw_Identifier') THEN + ALTER TABLE "Company" DROP COLUMN "cw_Identifier"; + END IF; + + -- Add new columns + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'dateDeleted') THEN + ALTER TABLE "Company" ADD COLUMN "dateDeleted" TIMESTAMP(3); + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'deleteFlag') THEN + ALTER TABLE "Company" ADD COLUMN "deleteFlag" BOOLEAN NOT NULL DEFAULT false; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'deletedAt') THEN + ALTER TABLE "Company" ADD COLUMN "deletedAt" TIMESTAMP(3); + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'deletedById') THEN + ALTER TABLE "Company" ADD COLUMN "deletedById" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'enteredById') THEN + ALTER TABLE "Company" ADD COLUMN "enteredById" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'phone') THEN + ALTER TABLE "Company" ADD COLUMN "phone" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'taxExempt') THEN + ALTER TABLE "Company" ADD COLUMN "taxExempt" BOOLEAN NOT NULL DEFAULT false; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'taxId') THEN + ALTER TABLE "Company" ADD COLUMN "taxId" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Company' AND column_name = 'website') THEN + ALTER TABLE "Company" ADD COLUMN "website" TEXT; + END IF; +END $$; + +CREATE UNIQUE INDEX IF NOT EXISTS "Company_id_key" ON "Company"("id"); + +-- ============================================================================= +-- SECTION 5: UnifiSite — change companyId from TEXT to INTEGER +-- ============================================================================= + +DO $$ BEGIN + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'UnifiSite' AND column_name = 'companyId' AND data_type = 'text' + ) THEN + ALTER TABLE "UnifiSite" DROP COLUMN "companyId"; + ALTER TABLE "UnifiSite" ADD COLUMN "companyId" INTEGER; + END IF; +END $$; + +-- ============================================================================= +-- SECTION 6: Opportunity — full restructure +-- ============================================================================= + +-- Drop FKs referencing Opportunity by old id/companyId +DO $$ BEGIN + IF EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'GeneratedQuotes_opportunityId_fkey') THEN + ALTER TABLE "GeneratedQuotes" DROP CONSTRAINT "GeneratedQuotes_opportunityId_fkey"; + END IF; +END $$; + +DROP INDEX IF EXISTS "Opportunity_cwOpportunityId_key"; + +DO $$ BEGIN + -- Add uid PK column if missing + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'uid') THEN + ALTER TABLE "Opportunity" ADD COLUMN "uid" TEXT NOT NULL DEFAULT ''; + END IF; + + -- Swap PK from id to uid + IF EXISTS ( + SELECT 1 FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name + WHERE tc.table_name = 'Opportunity' AND tc.constraint_type = 'PRIMARY KEY' AND kcu.column_name = 'id' + ) THEN + ALTER TABLE "Opportunity" DROP CONSTRAINT "Opportunity_pkey"; + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_pkey" PRIMARY KEY ("uid"); + END IF; + + -- Change id from TEXT to INTEGER + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'Opportunity' AND column_name = 'id' AND data_type = 'text' + ) THEN + ALTER TABLE "Opportunity" DROP COLUMN "id"; + ALTER TABLE "Opportunity" ADD COLUMN "id" INTEGER; + END IF; + + -- Change companyId from TEXT to INTEGER + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'Opportunity' AND column_name = 'companyId' AND data_type = 'text' + ) THEN + ALTER TABLE "Opportunity" DROP COLUMN "companyId"; + ALTER TABLE "Opportunity" ADD COLUMN "companyId" INTEGER; + END IF; + + -- Drop old flat/CW columns + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'cwOpportunityId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "cwOpportunityId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'campaignCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "campaignCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'campaignName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "campaignName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'closedByCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "closedByCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'closedByName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "closedByName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'companyCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "companyCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'companyName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "companyName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'contactCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "contactCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'contactName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "contactName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'cwLastUpdated') THEN + ALTER TABLE "Opportunity" DROP COLUMN "cwLastUpdated"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'departmentCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "departmentCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'departmentName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "departmentName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'locationCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "locationCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'locationName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "locationName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'primarySalesRepCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "primarySalesRepCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'primarySalesRepIdentifier') THEN + ALTER TABLE "Opportunity" DROP COLUMN "primarySalesRepIdentifier"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'primarySalesRepName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "primarySalesRepName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'priorityCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "priorityCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'priorityName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "priorityName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'ratingCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "ratingCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'ratingName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "ratingName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'secondarySalesRepCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "secondarySalesRepCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'secondarySalesRepIdentifier') THEN + ALTER TABLE "Opportunity" DROP COLUMN "secondarySalesRepIdentifier"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'secondarySalesRepName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "secondarySalesRepName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'siteCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "siteCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'siteName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "siteName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'statusCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "statusCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'statusName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "statusName"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'totalSalesTax') THEN + ALTER TABLE "Opportunity" DROP COLUMN "totalSalesTax"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'typeCwId') THEN + ALTER TABLE "Opportunity" DROP COLUMN "typeCwId"; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'typeName') THEN + ALTER TABLE "Opportunity" DROP COLUMN "typeName"; + END IF; + + -- Add new columns + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'closedById') THEN + ALTER TABLE "Opportunity" ADD COLUMN "closedById" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'contactId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "contactId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'departmentId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "departmentId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'eneteredBy') THEN + ALTER TABLE "Opportunity" ADD COLUMN "eneteredBy" TEXT NOT NULL DEFAULT ''; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'interest') THEN + ALTER TABLE "Opportunity" ADD COLUMN "interest" "OpportunityInterest"; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'locationId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "locationId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'oppNarrative') THEN + ALTER TABLE "Opportunity" ADD COLUMN "oppNarrative" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'primarySalesRepId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "primarySalesRepId" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'secondarySalesRepId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "secondarySalesRepId" TEXT; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'siteId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "siteId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'statusId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "statusId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'taxCodeId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "taxCodeId" INTEGER; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'typeId') THEN + ALTER TABLE "Opportunity" ADD COLUMN "typeId" INTEGER NOT NULL DEFAULT 0; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'updatedBy') THEN + ALTER TABLE "Opportunity" ADD COLUMN "updatedBy" TEXT NOT NULL DEFAULT ''; + END IF; +END $$; + +CREATE UNIQUE INDEX IF NOT EXISTS "Opportunity_id_key" ON "Opportunity"("id"); + +-- ============================================================================= +-- SECTION 7: New tables (all idempotent via IF NOT EXISTS) +-- ============================================================================= + +CREATE TABLE IF NOT EXISTS "CorporateLocation" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "updatedById" TEXT, + "addressLine1" TEXT, + "addressLine2" TEXT, + "city" TEXT, + "state" "USState", + "zipCode" TEXT, + "country" "Country", + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CorporateLocation_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CorporateLocation_id_key" ON "CorporateLocation"("id"); + +CREATE TABLE IF NOT EXISTS "InternalDepartment" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "InternalDepartment_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "InternalDepartment_id_key" ON "InternalDepartment"("id"); + +CREATE TABLE IF NOT EXISTS "CompanyAddress" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "addressLine1" TEXT, + "addressLine2" TEXT, + "city" TEXT, + "state" "USState", + "zipCode" TEXT, + "country" "Country", + "phone" TEXT, + "fax" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultMailFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultBillFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultShipFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "companyId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CompanyAddress_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CompanyAddress_id_key" ON "CompanyAddress"("id"); + +CREATE TABLE IF NOT EXISTS "Contact" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "active" BOOLEAN NOT NULL DEFAULT true, + "default" BOOLEAN NOT NULL DEFAULT false, + "firstName" TEXT NOT NULL, + "lastName" TEXT NOT NULL, + "nickname" TEXT, + "title" TEXT, + "gender" "GenderType", + "birthday" TIMESTAMP(3), + "email" TEXT, + "phone" TEXT, + "phoneExtension" TEXT, + "phoneType" "PhoneType", + "companyAddressId" INTEGER, + "memberId" INTEGER, + "companyId" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Contact_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "Contact_id_key" ON "Contact"("id"); + +CREATE TABLE IF NOT EXISTS "CatalogItemType" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CatalogItemType_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CatalogItemType_id_key" ON "CatalogItemType"("id"); + +CREATE TABLE IF NOT EXISTS "CatalogCategory" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CatalogCategory_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CatalogCategory_id_key" ON "CatalogCategory"("id"); + +CREATE TABLE IF NOT EXISTS "CatalogSubcategory" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "categoryId" INTEGER NOT NULL, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CatalogSubcategory_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CatalogSubcategory_id_key" ON "CatalogSubcategory"("id"); + +CREATE TABLE IF NOT EXISTS "CatalogManufacturer" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "CatalogManufacturer_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "CatalogManufacturer_id_key" ON "CatalogManufacturer"("id"); + +CREATE TABLE IF NOT EXISTS "Warehouse" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "lockedFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Warehouse_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "Warehouse_id_key" ON "Warehouse"("id"); + +CREATE TABLE IF NOT EXISTS "WarehouseBin" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "minQuantity" INTEGER NOT NULL, + "maxQuantity" INTEGER NOT NULL, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "warehouseId" INTEGER, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "WarehouseBin_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "WarehouseBin_id_key" ON "WarehouseBin"("id"); + +CREATE TABLE IF NOT EXISTS "ProductInventory" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "qtyOnHand" INTEGER NOT NULL DEFAULT 0, + "warehouseBinId" INTEGER NOT NULL, + "itemId" INTEGER, + "warehouseId" INTEGER, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ProductInventory_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ProductInventory_id_key" ON "ProductInventory"("id"); + +CREATE TABLE IF NOT EXISTS "MinimumStockByWarehouse" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "minQty" INTEGER NOT NULL DEFAULT 0, + "warehouseId" INTEGER NOT NULL, + "itemId" INTEGER, + "updatedById" TEXT, + "enteredById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "MinimumStockByWarehouse_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "MinimumStockByWarehouse_id_key" ON "MinimumStockByWarehouse"("id"); + +CREATE TABLE IF NOT EXISTS "ProductData" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "qty" DOUBLE PRECISION NOT NULL DEFAULT 1, + "internalNote" TEXT, + "shortDescription" TEXT, + "description" TEXT, + "sequenceNumber" INTEGER, + "procurementNotes" TEXT, + "productNarrative" TEXT, + "unitPrice" DOUBLE PRECISION NOT NULL DEFAULT 0, + "unitCost" DOUBLE PRECISION NOT NULL DEFAULT 0, + "listPrice" DOUBLE PRECISION NOT NULL DEFAULT 0, + "discount" DOUBLE PRECISION NOT NULL DEFAULT 0, + "recurringRevenue" DOUBLE PRECISION NOT NULL DEFAULT 0, + "recurringCost" DOUBLE PRECISION NOT NULL DEFAULT 0, + "qtyPicked" INTEGER NOT NULL DEFAULT 0, + "qtyShipped" INTEGER NOT NULL DEFAULT 0, + "cancelReason" TEXT, + "cancelQty" DOUBLE PRECISION, + "billableFlag" BOOLEAN NOT NULL DEFAULT true, + "taxableFlag" BOOLEAN NOT NULL DEFAULT true, + "invoiceFlag" BOOLEAN NOT NULL DEFAULT true, + "recurringFlag" BOOLEAN NOT NULL DEFAULT false, + "poApprovedFlag" BOOLEAN NOT NULL DEFAULT false, + "calcPriceFlag" BOOLEAN NOT NULL DEFAULT true, + "calcCostFlag" BOOLEAN NOT NULL DEFAULT true, + "cancelFlag" BOOLEAN NOT NULL DEFAULT false, + "catalogItemId" INTEGER NOT NULL, + "corporateLocationId" INTEGER NOT NULL, + "serviceTicketId" INTEGER, + "opportunityId" INTEGER, + "updatedById" TEXT, + "createdById" TEXT, + "closedById" TEXT, + "cancelById" TEXT NOT NULL DEFAULT '', + "closedAt" TIMESTAMP(3), + "cancelledAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ProductData_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ProductData_id_key" ON "ProductData"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketLocation" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketLocation_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketLocation_id_key" ON "ServiceTicketLocation"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketSource" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketSource_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketSource_id_key" ON "ServiceTicketSource"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketImpact" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketImpact_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketImpact_id_key" ON "ServiceTicketImpact"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketPriority" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "color" TEXT, + "description" TEXT, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketPriority_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketPriority_id_key" ON "ServiceTicketPriority"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketServerity" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketServerity_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketServerity_id_key" ON "ServiceTicketServerity"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketBoard" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "timeBillableFlag" BOOLEAN NOT NULL DEFAULT false, + "expenseBillableFlag" BOOLEAN NOT NULL DEFAULT false, + "productBillableFlag" BOOLEAN NOT NULL DEFAULT false, + "timeInvoiceableFlag" BOOLEAN NOT NULL DEFAULT false, + "expenseInvoiceableFlag" BOOLEAN NOT NULL DEFAULT false, + "productInvoiceableFlag" BOOLEAN NOT NULL DEFAULT false, + "autoAssignNewFlag" BOOLEAN NOT NULL DEFAULT false, + "autoAssignEmailCreatedFlag" BOOLEAN NOT NULL DEFAULT false, + "autoAssignPortalCreatedFlag" BOOLEAN NOT NULL DEFAULT false, + "projectFlag" BOOLEAN NOT NULL DEFAULT false, + "lockDescriptionFlag" BOOLEAN NOT NULL DEFAULT false, + "emailContactFlag" BOOLEAN NOT NULL DEFAULT false, + "emailResourceFlag" BOOLEAN NOT NULL DEFAULT false, + "resolutionSortOrder" CHAR(1) NOT NULL DEFAULT 'D', + "internalAnalysisSortOrder" CHAR(1) NOT NULL DEFAULT 'D', + "locationId" INTEGER NOT NULL, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketBoard_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketBoard_id_key" ON "ServiceTicketBoard"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketType" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketType_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketType_id_key" ON "ServiceTicketType"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicket" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "summary" TEXT NOT NULL, + "addressLine1" TEXT, + "addressLine2" TEXT, + "city" TEXT, + "state" "USState", + "zipCode" TEXT, + "country" "Country", + "contactName" TEXT, + "phone" TEXT, + "phoneExtension" TEXT, + "phoneType" "PhoneType", + "email" TEXT, + "poNumber" TEXT, + "billCompleteFlag" BOOLEAN NOT NULL DEFAULT false, + "billUnapprovedFlag" BOOLEAN NOT NULL DEFAULT false, + "billingAmount" DOUBLE PRECISION NOT NULL DEFAULT 0.00, + "billingMethod" "BillingMethod" NOT NULL DEFAULT 'ACTUAL_RATES', + "timeBillableFlag" BOOLEAN NOT NULL DEFAULT true, + "expenseBillableFlag" BOOLEAN NOT NULL DEFAULT true, + "productBillableFlag" BOOLEAN NOT NULL DEFAULT true, + "timeInvoiceableFlag" BOOLEAN NOT NULL DEFAULT true, + "expenseInvoiceableFlag" BOOLEAN NOT NULL DEFAULT true, + "productInvoiceableFlag" BOOLEAN NOT NULL DEFAULT true, + "dateRequested" TIMESTAMP(3), + "billingType" "BillingType" NOT NULL DEFAULT 'STANDARD', + "billingInstructions" TEXT, + "rejectedFlag" BOOLEAN NOT NULL DEFAULT false, + "closedFlag" BOOLEAN NOT NULL DEFAULT false, + "redFlag" BOOLEAN NOT NULL DEFAULT false, + "publishFlag" BOOLEAN NOT NULL DEFAULT false, + "ticketOwnerId" TEXT, + "serviceTicketBoardId" INTEGER, + "severityId" INTEGER NOT NULL DEFAULT 0, + "impactId" INTEGER NOT NULL DEFAULT 0, + "priorityId" INTEGER NOT NULL DEFAULT 0, + "sourceId" INTEGER NOT NULL DEFAULT 0, + "locationId" INTEGER NOT NULL DEFAULT 0, + "parentId" INTEGER, + "companyId" INTEGER, + "contactId" INTEGER, + "companyAddressId" INTEGER, + "billingCompanyId" INTEGER, + "billingAddressId" INTEGER, + "createdById" TEXT, + "updatedById" TEXT, + "closedById" TEXT, + "rejectedAt" TIMESTAMP(3), + "closedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicket_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicket_id_key" ON "ServiceTicket"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketNote" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "notes" TEXT NOT NULL, + "notesMd" TEXT NOT NULL, + "authorId" TEXT NOT NULL, + "problemFlag" BOOLEAN NOT NULL DEFAULT false, + "resolutionFlag" BOOLEAN NOT NULL DEFAULT false, + "internalAnalysisFlag" BOOLEAN NOT NULL DEFAULT false, + "internalMemberFlag" BOOLEAN NOT NULL DEFAULT false, + "createdByParentFlag" BOOLEAN NOT NULL DEFAULT false, + "mergedFlag" BOOLEAN NOT NULL DEFAULT false, + "bundledFlag" BOOLEAN NOT NULL DEFAULT false, + "serviceTicketId" INTEGER NOT NULL, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ServiceTicketNote_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ServiceTicketNote_id_key" ON "ServiceTicketNote"("id"); + +CREATE TABLE IF NOT EXISTS "ServiceTicketFinalData" ( + "id" TEXT NOT NULL, + CONSTRAINT "ServiceTicketFinalData_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE IF NOT EXISTS "OpportunityType" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "OpportunityType_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "OpportunityType_id_key" ON "OpportunityType"("id"); + +CREATE TABLE IF NOT EXISTS "OpportunityStatus" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "inactiveFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "wonFlag" BOOLEAN NOT NULL DEFAULT false, + "lostFlag" BOOLEAN NOT NULL DEFAULT false, + "closeFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "OpportunityStatus_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "OpportunityStatus_id_key" ON "OpportunityStatus"("id"); + +CREATE TABLE IF NOT EXISTS "ScheduleStatus" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "color" TEXT, + "softFlag" BOOLEAN NOT NULL DEFAULT false, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ScheduleStatus_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ScheduleStatus_id_key" ON "ScheduleStatus"("id"); + +CREATE TABLE IF NOT EXISTS "ScheduleType" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "displayColor" TEXT, + "tableReference" TEXT, + "moduleId" CHAR(2), + "scheduleTypeId" CHAR(1), + "systemFlag" BOOLEAN NOT NULL DEFAULT false, + "displayFlag" BOOLEAN NOT NULL DEFAULT false, + "updatedById" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "ScheduleType_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "ScheduleType_id_key" ON "ScheduleType"("id"); + +CREATE TABLE IF NOT EXISTS "ScheduleSpan" ( + "id" SERIAL NOT NULL, + "scheduleSpanId" CHAR(1), + "spanDesc" CHAR(20), + CONSTRAINT "ScheduleSpan_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE IF NOT EXISTS "Schedule" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "memberId" TEXT, + "closedFlag" BOOLEAN NOT NULL DEFAULT false, + "reminderFlag" BOOLEAN NOT NULL DEFAULT false, + "allDayFlag" BOOLEAN NOT NULL DEFAULT false, + "acknowledgementFlag" BOOLEAN NOT NULL DEFAULT false, + "meetingFlag" BOOLEAN NOT NULL DEFAULT false, + "recurringFlag" BOOLEAN NOT NULL DEFAULT false, + "billableFlag" BOOLEAN NOT NULL DEFAULT false, + "acknowledgedById" TEXT, + "acknowledgedAt" TIMESTAMP(3), + "startDate" TIMESTAMP(3), + "endDate" TIMESTAMP(3), + "hoursScheduled" DOUBLE PRECISION, + "duration" INTEGER, + "hoursPerDay" DOUBLE PRECISION, + "reminderMinutes" INTEGER DEFAULT 15, + "statusId" INTEGER, + "typeId" INTEGER, + "scheduleSpanId" INTEGER, + "updatedById" TEXT, + "createdById" TEXT, + "closedById" TEXT, + "closedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Schedule_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "Schedule_id_key" ON "Schedule"("id"); + +CREATE TABLE IF NOT EXISTS "TaxCode" ( + "id" INTEGER NOT NULL, + "uid" TEXT NOT NULL, + "code" TEXT NOT NULL, + "codeCaption" TEXT NOT NULL, + "description" TEXT, + "rate" DOUBLE PRECISION, + "defaultFlag" BOOLEAN NOT NULL DEFAULT false, + "createdBy" TEXT NOT NULL DEFAULT '', + "updatedBy" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "TaxCode_pkey" PRIMARY KEY ("uid") +); +CREATE UNIQUE INDEX IF NOT EXISTS "TaxCode_id_key" ON "TaxCode"("id"); +CREATE UNIQUE INDEX IF NOT EXISTS "TaxCode_code_key" ON "TaxCode"("code"); + +-- ============================================================================= +-- SECTION 8: Foreign keys (all idempotent via DO $$ ... $$) +-- ============================================================================= + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'UnifiSite_companyId_fkey') THEN + ALTER TABLE "UnifiSite" ADD CONSTRAINT "UnifiSite_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Company_deletedById_fkey') THEN + ALTER TABLE "Company" ADD CONSTRAINT "Company_deletedById_fkey" + FOREIGN KEY ("deletedById") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Company_enteredById_fkey') THEN + ALTER TABLE "Company" ADD CONSTRAINT "Company_enteredById_fkey" + FOREIGN KEY ("enteredById") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'CompanyAddress_updatedById_fkey') THEN + ALTER TABLE "CompanyAddress" ADD CONSTRAINT "CompanyAddress_updatedById_fkey" + FOREIGN KEY ("updatedById") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'CompanyAddress_companyId_fkey') THEN + ALTER TABLE "CompanyAddress" ADD CONSTRAINT "CompanyAddress_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Contact_companyAddressId_fkey') THEN + ALTER TABLE "Contact" ADD CONSTRAINT "Contact_companyAddressId_fkey" + FOREIGN KEY ("companyAddressId") REFERENCES "CompanyAddress"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Contact_companyId_fkey') THEN + ALTER TABLE "Contact" ADD CONSTRAINT "Contact_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'CatalogSubcategory_categoryId_fkey') THEN + ALTER TABLE "CatalogSubcategory" ADD CONSTRAINT "CatalogSubcategory_categoryId_fkey" + FOREIGN KEY ("categoryId") REFERENCES "CatalogCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'WarehouseBin_warehouseId_fkey') THEN + ALTER TABLE "WarehouseBin" ADD CONSTRAINT "WarehouseBin_warehouseId_fkey" + FOREIGN KEY ("warehouseId") REFERENCES "Warehouse"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductInventory_warehouseBinId_fkey') THEN + ALTER TABLE "ProductInventory" ADD CONSTRAINT "ProductInventory_warehouseBinId_fkey" + FOREIGN KEY ("warehouseBinId") REFERENCES "WarehouseBin"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductInventory_itemId_fkey') THEN + ALTER TABLE "ProductInventory" ADD CONSTRAINT "ProductInventory_itemId_fkey" + FOREIGN KEY ("itemId") REFERENCES "CatalogItem"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductInventory_warehouseId_fkey') THEN + ALTER TABLE "ProductInventory" ADD CONSTRAINT "ProductInventory_warehouseId_fkey" + FOREIGN KEY ("warehouseId") REFERENCES "Warehouse"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'MinimumStockByWarehouse_warehouseId_fkey') THEN + ALTER TABLE "MinimumStockByWarehouse" ADD CONSTRAINT "MinimumStockByWarehouse_warehouseId_fkey" + FOREIGN KEY ("warehouseId") REFERENCES "Warehouse"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'MinimumStockByWarehouse_itemId_fkey') THEN + ALTER TABLE "MinimumStockByWarehouse" ADD CONSTRAINT "MinimumStockByWarehouse_itemId_fkey" + FOREIGN KEY ("itemId") REFERENCES "CatalogItem"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'CatalogItem_subcategoryId_fkey') THEN + ALTER TABLE "CatalogItem" ADD CONSTRAINT "CatalogItem_subcategoryId_fkey" + FOREIGN KEY ("subcategoryId") REFERENCES "CatalogSubcategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'CatalogItem_manufacturerId_fkey') THEN + ALTER TABLE "CatalogItem" ADD CONSTRAINT "CatalogItem_manufacturerId_fkey" + FOREIGN KEY ("manufacturerId") REFERENCES "CatalogManufacturer"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductData_serviceTicketId_fkey') THEN + ALTER TABLE "ProductData" ADD CONSTRAINT "ProductData_serviceTicketId_fkey" + FOREIGN KEY ("serviceTicketId") REFERENCES "ServiceTicket"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductData_opportunityId_fkey') THEN + ALTER TABLE "ProductData" ADD CONSTRAINT "ProductData_opportunityId_fkey" + FOREIGN KEY ("opportunityId") REFERENCES "Opportunity"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductData_catalogItemId_fkey') THEN + ALTER TABLE "ProductData" ADD CONSTRAINT "ProductData_catalogItemId_fkey" + FOREIGN KEY ("catalogItemId") REFERENCES "CatalogItem"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ProductData_corporateLocationId_fkey') THEN + ALTER TABLE "ProductData" ADD CONSTRAINT "ProductData_corporateLocationId_fkey" + FOREIGN KEY ("corporateLocationId") REFERENCES "CorporateLocation"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_severityId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_severityId_fkey" + FOREIGN KEY ("severityId") REFERENCES "ServiceTicketServerity"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_impactId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_impactId_fkey" + FOREIGN KEY ("impactId") REFERENCES "ServiceTicketImpact"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_priorityId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_priorityId_fkey" + FOREIGN KEY ("priorityId") REFERENCES "ServiceTicketPriority"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_sourceId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_sourceId_fkey" + FOREIGN KEY ("sourceId") REFERENCES "ServiceTicketSource"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_locationId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_locationId_fkey" + FOREIGN KEY ("locationId") REFERENCES "ServiceTicketLocation"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_serviceTicketBoardId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_serviceTicketBoardId_fkey" + FOREIGN KEY ("serviceTicketBoardId") REFERENCES "ServiceTicketBoard"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_ticketOwnerId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_ticketOwnerId_fkey" + FOREIGN KEY ("ticketOwnerId") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_companyId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_contactId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_contactId_fkey" + FOREIGN KEY ("contactId") REFERENCES "Contact"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_companyAddressId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_companyAddressId_fkey" + FOREIGN KEY ("companyAddressId") REFERENCES "CompanyAddress"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_billingCompanyId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_billingCompanyId_fkey" + FOREIGN KEY ("billingCompanyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_billingAddressId_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_billingAddressId_fkey" + FOREIGN KEY ("billingAddressId") REFERENCES "CompanyAddress"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_closedById_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_closedById_fkey" + FOREIGN KEY ("closedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_createdById_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_createdById_fkey" + FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicket_updatedById_fkey') THEN + ALTER TABLE "ServiceTicket" ADD CONSTRAINT "ServiceTicket_updatedById_fkey" + FOREIGN KEY ("updatedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicketNote_authorId_fkey') THEN + ALTER TABLE "ServiceTicketNote" ADD CONSTRAINT "ServiceTicketNote_authorId_fkey" + FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicketNote_serviceTicketId_fkey') THEN + ALTER TABLE "ServiceTicketNote" ADD CONSTRAINT "ServiceTicketNote_serviceTicketId_fkey" + FOREIGN KEY ("serviceTicketId") REFERENCES "ServiceTicket"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'ServiceTicketBoard_locationId_fkey') THEN + ALTER TABLE "ServiceTicketBoard" ADD CONSTRAINT "ServiceTicketBoard_locationId_fkey" + FOREIGN KEY ("locationId") REFERENCES "CorporateLocation"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_typeId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_typeId_fkey" + FOREIGN KEY ("typeId") REFERENCES "OpportunityType"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_statusId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_statusId_fkey" + FOREIGN KEY ("statusId") REFERENCES "OpportunityStatus"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_taxCodeId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_taxCodeId_fkey" + FOREIGN KEY ("taxCodeId") REFERENCES "TaxCode"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_primarySalesRepId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_primarySalesRepId_fkey" + FOREIGN KEY ("primarySalesRepId") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_secondarySalesRepId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_secondarySalesRepId_fkey" + FOREIGN KEY ("secondarySalesRepId") REFERENCES "User"("cwIdentifier") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_companyId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_contactId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_contactId_fkey" + FOREIGN KEY ("contactId") REFERENCES "Contact"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_siteId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_siteId_fkey" + FOREIGN KEY ("siteId") REFERENCES "CompanyAddress"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_locationId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_locationId_fkey" + FOREIGN KEY ("locationId") REFERENCES "CorporateLocation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Opportunity_departmentId_fkey') THEN + ALTER TABLE "Opportunity" ADD CONSTRAINT "Opportunity_departmentId_fkey" + FOREIGN KEY ("departmentId") REFERENCES "InternalDepartment"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Schedule_statusId_fkey') THEN + ALTER TABLE "Schedule" ADD CONSTRAINT "Schedule_statusId_fkey" + FOREIGN KEY ("statusId") REFERENCES "ScheduleStatus"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Schedule_typeId_fkey') THEN + ALTER TABLE "Schedule" ADD CONSTRAINT "Schedule_typeId_fkey" + FOREIGN KEY ("typeId") REFERENCES "ScheduleType"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Schedule_scheduleSpanId_fkey') THEN + ALTER TABLE "Schedule" ADD CONSTRAINT "Schedule_scheduleSpanId_fkey" + FOREIGN KEY ("scheduleSpanId") REFERENCES "ScheduleSpan"("id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'Credential_companyId_fkey') THEN + ALTER TABLE "Credential" ADD CONSTRAINT "Credential_companyId_fkey" + FOREIGN KEY ("companyId") REFERENCES "Company"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'GeneratedQuotes_opportunityId_fkey') THEN + ALTER TABLE "GeneratedQuotes" ADD CONSTRAINT "GeneratedQuotes_opportunityId_fkey" + FOREIGN KEY ("opportunityId") REFERENCES "Opportunity"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = '_LinkedItems_A_fkey') THEN + ALTER TABLE "_LinkedItems" ADD CONSTRAINT "_LinkedItems_A_fkey" + FOREIGN KEY ("A") REFERENCES "CatalogItem"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = '_LinkedItems_B_fkey') THEN + ALTER TABLE "_LinkedItems" ADD CONSTRAINT "_LinkedItems_B_fkey" + FOREIGN KEY ("B") REFERENCES "CatalogItem"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + END IF; +END $$; + +-- ============================================================================= +-- SECTION 9: Cleanup — drop temporary defaults added for idempotency, +-- enforce NOT NULL on new integer id columns, drop removed columns +-- ============================================================================= + +-- CatalogItem: drop defaults, enforce NOT NULL +ALTER TABLE "CatalogItem" ALTER COLUMN "uid" DROP DEFAULT; +ALTER TABLE "CatalogItem" ALTER COLUMN "id" SET NOT NULL; +ALTER TABLE "CatalogItem" ALTER COLUMN "subcategoryId" DROP DEFAULT; + +-- Company: drop defaults, enforce NOT NULL +ALTER TABLE "Company" ALTER COLUMN "uid" DROP DEFAULT; +ALTER TABLE "Company" ALTER COLUMN "id" SET NOT NULL; + +-- Opportunity: drop defaults, enforce NOT NULL, drop the old cwDateEntered column +DO $$ BEGIN + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'Opportunity' AND column_name = 'cwDateEntered') THEN + ALTER TABLE "Opportunity" DROP COLUMN "cwDateEntered"; + END IF; +END $$; +ALTER TABLE "Opportunity" ALTER COLUMN "uid" DROP DEFAULT; +ALTER TABLE "Opportunity" ALTER COLUMN "id" SET NOT NULL; +ALTER TABLE "Opportunity" ALTER COLUMN "eneteredBy" DROP DEFAULT; +ALTER TABLE "Opportunity" ALTER COLUMN "typeId" DROP DEFAULT; +ALTER TABLE "Opportunity" ALTER COLUMN "updatedBy" DROP DEFAULT; + +-- ProductData: drop the sentinel default on cancelById +ALTER TABLE "ProductData" ALTER COLUMN "cancelById" DROP DEFAULT; + +-- ServiceTicket: drop sentinel defaults on FK columns +ALTER TABLE "ServiceTicket" ALTER COLUMN "severityId" DROP DEFAULT; +ALTER TABLE "ServiceTicket" ALTER COLUMN "impactId" DROP DEFAULT; +ALTER TABLE "ServiceTicket" ALTER COLUMN "priorityId" DROP DEFAULT; +ALTER TABLE "ServiceTicket" ALTER COLUMN "sourceId" DROP DEFAULT; +ALTER TABLE "ServiceTicket" ALTER COLUMN "locationId" DROP DEFAULT; + +-- TaxCode: drop sentinel default on createdBy +ALTER TABLE "TaxCode" ALTER COLUMN "createdBy" DROP DEFAULT;