-- ============================================================ -- CRM Backend Refactor Migration -- - UUID conversion for all IDs -- - Multi-tenant branch support (alla, onvalla) -- - Dual customer codes (CRM + ERP) -- - Contact visibility and sharing -- - Multi-currency quotations with revisions -- ============================================================ BEGIN; -- ============================================================ -- PHASE 1: Create Branches Table -- ============================================================ CREATE TABLE IF NOT EXISTS ms_branches ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Insert initial branches INSERT INTO ms_branches (code, name, is_active) VALUES ('alla', 'Alla Branch', TRUE), ('onvalla', 'Onvalla Branch', TRUE) ON CONFLICT (code) DO NOTHING; -- Create indexes CREATE INDEX IF NOT EXISTS idx_branches_code ON ms_branches(code); CREATE INDEX IF NOT EXISTS idx_branches_is_active ON ms_branches(is_active); -- ============================================================ -- PHASE 2: Prepare Customers Table for UUID and Branch -- ============================================================ -- Add new UUID column (will become primary key) ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); -- Add branch ID column (nullable initially) ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS branch_id UUID REFERENCES ms_branches(id); -- Add dual customer code columns ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS crm_customer_code TEXT; ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS erp_customer_code TEXT; -- Update credit limit to numeric if it's currently text DO $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'ms_customers' AND column_name = 'credit_limit' AND data_type = 'text' ) THEN ALTER TABLE ms_customers ALTER COLUMN credit_limit TYPE NUMERIC(15,2) USING credit_limit::NUMERIC(15,2); END IF; END $$; -- Update user references to UUID ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS created_by_new UUID REFERENCES users(id); ALTER TABLE ms_customers ADD COLUMN IF NOT EXISTS updated_by_new UUID REFERENCES users(id); -- ============================================================ -- PHASE 3: Prepare Customer Contacts Table -- ============================================================ -- Add new UUID column ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); -- Add branch ID ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS branch_id UUID REFERENCES ms_branches(id); -- Update customer reference to UUID ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS customer_id_new UUID REFERENCES ms_customers(id); -- Add visibility fields ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS is_public BOOLEAN DEFAULT FALSE; -- Update user references to UUID ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS created_by_new UUID REFERENCES users(id); ALTER TABLE ms_customer_contacts ADD COLUMN IF NOT EXISTS updated_by_new UUID REFERENCES users(id); -- ============================================================ -- PHASE 4: Create Contact Shares Table -- ============================================================ CREATE TABLE IF NOT EXISTS ms_customer_contact_shares ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), contact_id UUID NOT NULL REFERENCES ms_customer_contacts(id) ON DELETE CASCADE, shared_with_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, shared_by UUID NOT NULL REFERENCES users(id), shared_at TIMESTAMP DEFAULT NOW(), notes TEXT, UNIQUE(contact_id, shared_with_user_id) ); CREATE INDEX IF NOT EXISTS idx_contact_shares_contact ON ms_customer_contact_shares(contact_id); CREATE INDEX IF NOT EXISTS idx_contact_shares_user ON ms_customer_contact_shares(shared_with_user_id); CREATE INDEX IF NOT EXISTS idx_contact_shares_shared_by ON ms_customer_contact_shares(shared_by); -- ============================================================ -- PHASE 5: Prepare Quotations Table -- ============================================================ -- Add new UUID column ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); -- Add branch ID ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS branch_id UUID REFERENCES ms_branches(id); -- Add revision fields ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS revision_no INTEGER DEFAULT 1 NOT NULL; ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS parent_quotation_id_new UUID REFERENCES tr_quotations(id); -- Add multi-currency fields ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS currency_code TEXT NOT NULL DEFAULT 'THB'; ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS exchange_rate NUMERIC(12,6) NOT NULL DEFAULT 1.0; ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS base_currency_amount NUMERIC(15,2); -- Update user references to UUID ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS salesman_id_new UUID REFERENCES users(id); ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS sale_admin_id_new UUID REFERENCES users(id); ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS created_by_new UUID REFERENCES users(id); ALTER TABLE tr_quotations ADD COLUMN IF NOT EXISTS updated_by_new UUID REFERENCES users(id); -- ============================================================ -- PHASE 6: Prepare Quotation Items Table -- ============================================================ -- Add new UUID column ALTER TABLE tr_quotations_items ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); -- Update quotation reference to UUID ALTER TABLE tr_quotations_items ADD COLUMN IF NOT EXISTS quotation_id_new UUID REFERENCES tr_quotations(id); -- ============================================================ -- PHASE 7: Prepare Quotation Customers Table -- ============================================================ -- Add new UUID column ALTER TABLE tr_quotations_customers ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); -- Update references to UUID ALTER TABLE tr_quotations_customers ADD COLUMN IF NOT EXISTS quotation_id_new UUID REFERENCES tr_quotations(id); ALTER TABLE tr_quotations_customers ADD COLUMN IF NOT EXISTS customer_id_new UUID REFERENCES ms_customers(id); -- ============================================================ -- PHASE 8: Prepare Additional Quotation Tables for UUID -- ============================================================ -- Quotation Followups ALTER TABLE tr_quotations_followups ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_followups ADD COLUMN IF NOT EXISTS quotation_id_new UUID REFERENCES tr_quotations(id); -- Quotation Attachments ALTER TABLE tr_quotations_attachments ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_attachments ADD COLUMN IF NOT EXISTS quotation_id_new UUID REFERENCES tr_quotations(id); -- Quotation Topics ALTER TABLE tr_quotations_topics ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_topics ADD COLUMN IF NOT EXISTS quotation_id_new UUID REFERENCES tr_quotations(id); -- Quotation Topic Items ALTER TABLE tr_quotations_topic_items ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_topic_items ADD COLUMN IF NOT EXISTS topic_id_new UUID REFERENCES tr_quotations_topics(id); -- Quotation Template Versions ALTER TABLE ms_quotations_template_versions ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_versions ADD COLUMN IF NOT EXISTS template_id_new UUID REFERENCES ms_quotations_templates(id); -- Quotation Template Mappings ALTER TABLE ms_quotations_template_mappings ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_mappings ADD COLUMN IF NOT EXISTS template_version_id_new UUID REFERENCES ms_quotations_template_versions(id); -- Quotation Template Table Columns ALTER TABLE ms_quotations_template_table_columns ADD COLUMN IF NOT EXISTS new_id UUID DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_table_columns ADD COLUMN IF NOT EXISTS mapping_id_new UUID REFERENCES ms_quotations_template_mappings(id); -- ============================================================ -- PHASE 9: Backfill Data -- ============================================================ -- Get alla branch ID (will be used as default) DO $$ DECLARE alla_branch_id UUID; BEGIN SELECT id INTO alla_branch_id FROM ms_branches WHERE code = 'alla' LIMIT 1; IF alla_branch_id IS NOT NULL THEN -- Backfill customers branch_id (default to alla) UPDATE ms_customers SET branch_id = alla_branch_id WHERE branch_id IS NULL; -- Generate CRM customer codes from existing codes UPDATE ms_customers SET crm_customer_code = code WHERE crm_customer_code IS NULL AND code IS NOT NULL; -- Backfill customer_contacts branch_id and customer_id UPDATE ms_customer_contacts cc SET branch_id = c.branch_id, customer_id_new = c.new_id FROM ms_customers c WHERE cc.customer_id = c.id::INTEGER; -- Backfill quotations branch_id UPDATE tr_quotations q SET branch_id = alla_branch_id WHERE branch_id IS NULL; -- Backfill quotation_items quotation_id UPDATE tr_quotations_items qi SET quotation_id_new = q.new_id FROM tr_quotations q WHERE qi.quotation_id = q.id::INTEGER; -- Backfill quotation_customers references UPDATE tr_quotations_customers qc SET quotation_id_new = q.new_id, customer_id_new = c.new_id FROM tr_quotations q JOIN tr_quotations_customers qcc ON qcc.quotation_id = q.id::INTEGER JOIN ms_customers c ON c.id::INTEGER = qcc.customer_id WHERE qc.id = qcc.id; -- Backfill quotation_followups UPDATE tr_quotations_followups qf SET quotation_id_new = q.new_id FROM tr_quotations q WHERE qf.quotation_id = q.id::INTEGER; -- Backfill quotation_attachments UPDATE tr_quotations_attachments qa SET quotation_id_new = q.new_id FROM tr_quotations q WHERE qa.quotation_id = q.id::INTEGER; -- Backfill quotation_topics UPDATE tr_quotations_topics qt SET quotation_id_new = q.new_id FROM tr_quotations q WHERE qt.quotation_id = q.id::INTEGER; -- Backfill quotation_topic_items UPDATE tr_quotations_topic_items qti SET topic_id_new = qt.new_id FROM tr_quotations_topics qt WHERE qti.topic_id = qt.id::INTEGER; -- Backfill quotation_template_versions UPDATE ms_quotations_template_versions qtv SET template_id_new = qt.id::UUID FROM ms_quotations_templates qt WHERE qtv.template_id = qt.id::INTEGER; -- Backfill quotation_template_mappings UPDATE ms_quotations_template_mappings qtm SET template_version_id_new = qtv.new_id FROM ms_quotations_template_versions qtv WHERE qtm.template_version_id = qtv.id::INTEGER; -- Backfill quotation_template_table_columns UPDATE ms_quotations_template_table_columns qttc SET mapping_id_new = qtm.new_id FROM ms_quotations_template_mappings qtm WHERE qttc.mapping_id = qtm.id::INTEGER; END IF; END $$; -- ============================================================ -- PHASE 10: Swap Columns - Customers -- ============================================================ -- Drop old constraints ALTER TABLE ms_customers DROP CONSTRAINT IF EXISTS ms_customers_pkey; ALTER TABLE ms_customers DROP CONSTRAINT IF EXISTS ms_customers_code_key; -- Drop old columns ALTER TABLE ms_customers DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE ms_customers DROP COLUMN IF EXISTS code CASCADE; -- Rename new columns ALTER TABLE ms_customers RENAME COLUMN new_id TO id; ALTER TABLE ms_customers RENAME COLUMN crm_customer_code TO code; -- Make new columns NOT NULL ALTER TABLE ms_customers ALTER COLUMN id SET NOT NULL; ALTER TABLE ms_customers ALTER COLUMN code SET NOT NULL; ALTER TABLE ms_customers ALTER COLUMN branch_id SET NOT NULL; -- Set default for UUID ALTER TABLE ms_customers ALTER COLUMN id SET DEFAULT gen_random_uuid(); -- Add back constraints ALTER TABLE ms_customers ADD PRIMARY KEY (id); ALTER TABLE ms_customers ADD UNIQUE (code); ALTER TABLE ms_customers ADD UNIQUE (erp_customer_code); -- Drop old user reference columns ALTER TABLE ms_customers DROP COLUMN IF EXISTS created_by CASCADE; ALTER TABLE ms_customers DROP COLUMN IF EXISTS updated_by CASCADE; -- Rename new user reference columns ALTER TABLE ms_customers RENAME COLUMN created_by_new TO created_by; ALTER TABLE ms_customers RENAME COLUMN updated_by_new TO updated_by; -- ============================================================ -- PHASE 11: Swap Columns - Customer Contacts -- ============================================================ -- Drop old constraints ALTER TABLE ms_customer_contacts DROP CONSTRAINT IF EXISTS ms_customer_contacts_pkey; ALTER TABLE ms_customer_contacts DROP CONSTRAINT IF EXISTS ms_customer_contacts_customer_id_fkey; -- Drop old columns ALTER TABLE ms_customer_contacts DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE ms_customer_contacts DROP COLUMN IF EXISTS customer_id CASCADE; ALTER TABLE ms_customer_contacts DROP COLUMN IF EXISTS created_by CASCADE; ALTER TABLE ms_customer_contacts DROP COLUMN IF EXISTS updated_by CASCADE; -- Rename new columns ALTER TABLE ms_customer_contacts RENAME COLUMN new_id TO id; ALTER TABLE ms_customer_contacts RENAME COLUMN customer_id_new TO customer_id; ALTER TABLE ms_customer_contacts RENAME COLUMN created_by_new TO created_by; ALTER TABLE ms_customer_contacts RENAME COLUMN updated_by_new TO updated_by; -- Make new columns NOT NULL ALTER TABLE ms_customer_contacts ALTER COLUMN id SET NOT NULL; ALTER TABLE ms_customer_contacts ALTER COLUMN customer_id SET NOT NULL; ALTER TABLE ms_customer_contacts ALTER COLUMN created_by SET NOT NULL; ALTER TABLE ms_customer_contacts ALTER COLUMN branch_id SET NOT NULL; -- Set default for UUID ALTER TABLE ms_customer_contacts ALTER COLUMN id SET DEFAULT gen_random_uuid(); -- Add back constraints ALTER TABLE ms_customer_contacts ADD PRIMARY KEY (id); -- ============================================================ -- PHASE 12: Swap Columns - Quotations -- ============================================================ -- Drop old constraints ALTER TABLE tr_quotations DROP CONSTRAINT IF EXISTS tr_quotations_pkey; ALTER TABLE tr_quotations DROP CONSTRAINT IF EXISTS tr_quotations_code_key; -- Drop old columns ALTER TABLE tr_quotations DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS code CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS parent_quotation_id CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS salesman_id CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS sale_admin_id CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS created_by CASCADE; ALTER TABLE tr_quotations DROP COLUMN IF EXISTS updated_by CASCADE; -- Rename new columns ALTER TABLE tr_quotations RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations RENAME COLUMN parent_quotation_id_new TO parent_quotation_id; ALTER TABLE tr_quotations RENAME COLUMN salesman_id_new TO salesman_id; ALTER TABLE tr_quotations RENAME COLUMN sale_admin_id_new TO sale_admin_id; ALTER TABLE tr_quotations RENAME COLUMN created_by_new TO created_by; ALTER TABLE tr_quotations RENAME COLUMN updated_by_new TO updated_by; -- Make new columns NOT NULL ALTER TABLE tr_quotations ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations ALTER COLUMN code SET NOT NULL; ALTER TABLE tr_quotations ALTER COLUMN branch_id SET NOT NULL; -- Set default for UUID ALTER TABLE tr_quotations ALTER COLUMN id SET DEFAULT gen_random_uuid(); -- Add back constraints ALTER TABLE tr_quotations ADD PRIMARY KEY (id); -- Note: code is NO LONGER unique (multi-currency support) -- ============================================================ -- PHASE 13: Swap Columns - Quotation Items -- ============================================================ -- Drop old constraints ALTER TABLE tr_quotations_items DROP CONSTRAINT IF EXISTS tr_quotations_items_pkey; ALTER TABLE tr_quotations_items DROP CONSTRAINT IF EXISTS tr_quotations_items_quotation_id_fkey; -- Drop old columns ALTER TABLE tr_quotations_items DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_items DROP COLUMN IF EXISTS quotation_id CASCADE; -- Rename new columns ALTER TABLE tr_quotations_items RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_items RENAME COLUMN quotation_id_new TO quotation_id; -- Make new columns NOT NULL ALTER TABLE tr_quotations_items ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_items ALTER COLUMN quotation_id SET NOT NULL; -- Set default for UUID ALTER TABLE tr_quotations_items ALTER COLUMN id SET DEFAULT gen_random_uuid(); -- Add back constraints ALTER TABLE tr_quotations_items ADD PRIMARY KEY (id); -- ============================================================ -- PHASE 14: Swap Columns - Quotation Customers -- ============================================================ -- Drop old constraints ALTER TABLE tr_quotations_customers DROP CONSTRAINT IF EXISTS tr_quotations_customers_pkey; ALTER TABLE tr_quotations_customers DROP CONSTRAINT IF EXISTS tr_quotations_customers_quotation_id_fkey; ALTER TABLE tr_quotations_customers DROP CONSTRAINT IF EXISTS tr_quotations_customers_customer_id_fkey; -- Drop old columns ALTER TABLE tr_quotations_customers DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_customers DROP COLUMN IF EXISTS quotation_id CASCADE; ALTER TABLE tr_quotations_customers DROP COLUMN IF EXISTS customer_id CASCADE; -- Rename new columns ALTER TABLE tr_quotations_customers RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_customers RENAME COLUMN quotation_id_new TO quotation_id; ALTER TABLE tr_quotations_customers RENAME COLUMN customer_id_new TO customer_id; -- Make new columns NOT NULL ALTER TABLE tr_quotations_customers ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_customers ALTER COLUMN quotation_id SET NOT NULL; ALTER TABLE tr_quotations_customers ALTER COLUMN customer_id SET NOT NULL; -- Set default for UUID ALTER TABLE tr_quotations_customers ALTER COLUMN id SET DEFAULT gen_random_uuid(); -- Add back constraints ALTER TABLE tr_quotations_customers ADD PRIMARY KEY (id); ALTER TABLE tr_quotations_customers ADD UNIQUE (quotation_id, customer_id, role); -- ============================================================ -- PHASE 15: Swap Columns - Additional Tables -- ============================================================ -- Quotation Followups ALTER TABLE tr_quotations_followups DROP CONSTRAINT IF EXISTS tr_quotations_followups_pkey; ALTER TABLE tr_quotations_followups DROP CONSTRAINT IF EXISTS tr_quotations_followups_quotation_id_fkey; ALTER TABLE tr_quotations_followups DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_followups DROP COLUMN IF EXISTS quotation_id CASCADE; ALTER TABLE tr_quotations_followups RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_followups RENAME COLUMN quotation_id_new TO quotation_id; ALTER TABLE tr_quotations_followups ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_followups ALTER COLUMN quotation_id SET NOT NULL; ALTER TABLE tr_quotations_followups ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_followups ADD PRIMARY KEY (id); -- Quotation Attachments ALTER TABLE tr_quotations_attachments DROP CONSTRAINT IF EXISTS tr_quotations_attachments_pkey; ALTER TABLE tr_quotations_attachments DROP CONSTRAINT IF EXISTS tr_quotations_attachments_quotation_id_fkey; ALTER TABLE tr_quotations_attachments DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_attachments DROP COLUMN IF EXISTS quotation_id CASCADE; ALTER TABLE tr_quotations_attachments RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_attachments RENAME COLUMN quotation_id_new TO quotation_id; ALTER TABLE tr_quotations_attachments ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_attachments ALTER COLUMN quotation_id SET NOT NULL; ALTER TABLE tr_quotations_attachments ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_attachments ADD PRIMARY KEY (id); -- Quotation Topics ALTER TABLE tr_quotations_topics DROP CONSTRAINT IF EXISTS tr_quotations_topics_pkey; ALTER TABLE tr_quotations_topics DROP CONSTRAINT IF EXISTS tr_quotations_topics_quotation_id_fkey; ALTER TABLE tr_quotations_topics DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_topics DROP COLUMN IF EXISTS quotation_id CASCADE; ALTER TABLE tr_quotations_topics RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_topics RENAME COLUMN quotation_id_new TO quotation_id; ALTER TABLE tr_quotations_topics ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_topics ALTER COLUMN quotation_id SET NOT NULL; ALTER TABLE tr_quotations_topics ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_topics ADD PRIMARY KEY (id); -- Quotation Topic Items ALTER TABLE tr_quotations_topic_items DROP CONSTRAINT IF EXISTS tr_quotations_topic_items_pkey; ALTER TABLE tr_quotations_topic_items DROP CONSTRAINT IF EXISTS tr_quotations_topic_items_topic_id_fkey; ALTER TABLE tr_quotations_topic_items DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE tr_quotations_topic_items DROP COLUMN IF EXISTS topic_id CASCADE; ALTER TABLE tr_quotations_topic_items RENAME COLUMN new_id TO id; ALTER TABLE tr_quotations_topic_items RENAME COLUMN topic_id_new TO topic_id; ALTER TABLE tr_quotations_topic_items ALTER COLUMN id SET NOT NULL; ALTER TABLE tr_quotations_topic_items ALTER COLUMN topic_id SET NOT NULL; ALTER TABLE tr_quotations_topic_items ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE tr_quotations_topic_items ADD PRIMARY KEY (id); -- Quotation Template Versions ALTER TABLE ms_quotations_template_versions DROP CONSTRAINT IF EXISTS ms_quotations_template_versions_pkey; ALTER TABLE ms_quotations_template_versions DROP CONSTRAINT IF EXISTS ms_quotations_template_versions_template_id_fkey; ALTER TABLE ms_quotations_template_versions DROP CONSTRAINT IF EXISTS id CASCADE; ALTER TABLE ms_quotations_template_versions DROP CONSTRAINT IF EXISTS template_id CASCADE; ALTER TABLE ms_quotations_template_versions RENAME COLUMN new_id TO id; ALTER TABLE ms_quotations_template_versions RENAME COLUMN template_id_new TO template_id; ALTER TABLE ms_quotations_template_versions ALTER COLUMN id SET NOT NULL; ALTER TABLE ms_quotations_template_versions ALTER COLUMN template_id SET NOT NULL; ALTER TABLE ms_quotations_template_versions ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_versions ADD PRIMARY KEY (id); ALTER TABLE ms_quotations_template_versions ADD UNIQUE (template_id, version); -- Quotation Template Mappings ALTER TABLE ms_quotations_template_mappings DROP CONSTRAINT IF EXISTS ms_quotations_template_mappings_pkey; ALTER TABLE ms_quotations_template_mappings DROP CONSTRAINT IF EXISTS ms_quotations_template_mappings_template_version_id_fkey; ALTER TABLE ms_quotations_template_mappings DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE ms_quotations_template_mappings DROP COLUMN IF EXISTS template_version_id CASCADE; ALTER TABLE ms_quotations_template_mappings RENAME COLUMN new_id TO id; ALTER TABLE ms_quotations_template_mappings RENAME COLUMN template_version_id_new TO template_version_id; ALTER TABLE ms_quotations_template_mappings ALTER COLUMN id SET NOT NULL; ALTER TABLE ms_quotations_template_mappings ALTER COLUMN template_version_id SET NOT NULL; ALTER TABLE ms_quotations_template_mappings ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_mappings ADD PRIMARY KEY (id); -- Quotation Template Table Columns ALTER TABLE ms_quotations_template_table_columns DROP CONSTRAINT IF EXISTS ms_quotations_template_table_columns_pkey; ALTER TABLE ms_quotations_template_table_columns DROP CONSTRAINT IF EXISTS ms_quotations_template_table_columns_mapping_id_fkey; ALTER TABLE ms_quotations_template_table_columns DROP COLUMN IF EXISTS id CASCADE; ALTER TABLE ms_quotations_template_table_columns DROP COLUMN IF EXISTS mapping_id CASCADE; ALTER TABLE ms_quotations_template_table_columns RENAME COLUMN new_id TO id; ALTER TABLE ms_quotations_template_table_columns RENAME COLUMN mapping_id_new TO mapping_id; ALTER TABLE ms_quotations_template_table_columns ALTER COLUMN id SET NOT NULL; ALTER TABLE ms_quotations_template_table_columns ALTER COLUMN mapping_id SET NOT NULL; ALTER TABLE ms_quotations_template_table_columns ALTER COLUMN id SET DEFAULT gen_random_uuid(); ALTER TABLE ms_quotations_template_table_columns ADD PRIMARY KEY (id); -- ============================================================ -- PHASE 16: Create Quotation Contacts Snapshot Table -- ============================================================ CREATE TABLE IF NOT EXISTS tr_quotation_contacts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), quotation_id UUID NOT NULL REFERENCES tr_quotations(id) ON DELETE CASCADE, contact_id UUID REFERENCES ms_customer_contacts(id), snapshot_name TEXT NOT NULL, snapshot_email TEXT, snapshot_phone TEXT, snapshot_mobile TEXT, snapshot_position TEXT, snapshot_department TEXT, created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_quotation_contact ON tr_quotation_contacts(quotation_id); CREATE INDEX IF NOT EXISTS idx_quotation_contact_contact ON tr_quotation_contacts(contact_id); -- ============================================================ -- PHASE 17: Create Performance Indexes -- ============================================================ -- Customers indexes CREATE INDEX IF NOT EXISTS idx_customers_branch ON ms_customers(branch_id); CREATE INDEX IF NOT EXISTS idx_customers_status ON ms_customers(customer_status); CREATE INDEX IF NOT EXISTS idx_customers_crm_code ON ms_customers(code); CREATE INDEX IF NOT EXISTS idx_customers_erp_code ON ms_customers(erp_customer_code); -- Customer contacts indexes CREATE INDEX IF NOT EXISTS idx_contacts_customer ON ms_customer_contacts(customer_id); CREATE INDEX IF NOT EXISTS idx_contacts_branch ON ms_customer_contacts(branch_id); CREATE INDEX IF NOT EXISTS idx_contacts_created_by ON ms_customer_contacts(created_by); CREATE INDEX IF NOT EXISTS idx_contacts_visibility ON ms_customer_contacts(customer_id, created_by); -- Quotations indexes CREATE INDEX IF NOT EXISTS idx_quotations_branch ON tr_quotations(branch_id); CREATE INDEX IF NOT EXISTS idx_quotations_code ON tr_quotations(code); CREATE INDEX IF NOT EXISTS idx_quotation_status ON tr_quotations(status); CREATE INDEX IF NOT EXISTS idx_quotation_date ON tr_quotations(quotation_date); CREATE INDEX IF NOT EXISTS idx_quotations_branch_status ON tr_quotations(branch_id, status); CREATE INDEX IF NOT EXISTS idx_quotations_revision ON tr_quotations(parent_quotation_id); -- Quotation items indexes CREATE INDEX IF NOT EXISTS idx_qitem_quotation_id ON tr_quotations_items(quotation_id); -- Quotation customers indexes CREATE INDEX IF NOT EXISTS idx_qcust_quotation_id ON tr_quotations_customers(quotation_id); CREATE INDEX IF NOT EXISTS idx_qcust_customer_id ON tr_quotations_customers(customer_id); COMMIT; -- ============================================================ -- MIGRATION COMPLETE -- ============================================================ -- Verify migration -- SELECT COUNT(*) FROM ms_branches; -- SELECT COUNT(*) FROM ms_customers WHERE branch_id IS NULL; -- SELECT COUNT(*) FROM tr_quotations WHERE branch_id IS NULL; -- SELECT COUNT(*) FROM ms_customer_contacts WHERE branch_id IS NULL;