This commit is contained in:
phaichayon
2026-04-26 00:15:22 +07:00
parent a330abf9b6
commit 043edff93a
64 changed files with 25076 additions and 461 deletions

View File

@@ -0,0 +1,419 @@
CREATE TABLE "tr_audit_logs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"actor_id" text,
"entity_type" text NOT NULL,
"entity_id" text NOT NULL,
"action" text NOT NULL,
"before_data" text,
"after_data" text,
"ip_address" text,
"user_agent" text,
"request_id" text,
"created_at" timestamp DEFAULT now(),
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "ms_branches" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"code" text NOT NULL,
"name" text NOT NULL,
"is_active" boolean DEFAULT true,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
CONSTRAINT "ms_branches_code_unique" UNIQUE("code")
);
--> statement-breakpoint
CREATE TABLE "ms_customer_contact_shares" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"contact_id" uuid NOT NULL,
"shared_with_user_id" uuid NOT NULL,
"shared_by" uuid NOT NULL,
"shared_at" timestamp DEFAULT now(),
"notes" text,
CONSTRAINT "uq_contact_share" UNIQUE("contact_id","shared_with_user_id")
);
--> statement-breakpoint
CREATE TABLE "ms_customer_contacts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"branch_id" uuid NOT NULL,
"customer_id" uuid NOT NULL,
"name" text NOT NULL,
"position" text,
"department" text,
"phone" text,
"mobile" text,
"email" text,
"is_primary" boolean DEFAULT false,
"notes" text,
"created_by" uuid NOT NULL,
"is_public" boolean DEFAULT false,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"updated_by" uuid,
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "ms_customers" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"branch_id" uuid NOT NULL,
"crm_customer_code" text NOT NULL,
"erp_customer_code" text,
"name" text NOT NULL,
"abbr" text,
"tax_id" text,
"address" text,
"province" text,
"district" text,
"sub_district" text,
"postal_code" text,
"country" text DEFAULT 'Thailand',
"phone" text,
"email" text,
"website" text,
"customer_type" text,
"customer_old" boolean DEFAULT false,
"customer_ref" text,
"customer_status" text DEFAULT 'draft',
"lead_channel" text,
"awareness" text,
"customer_group" text,
"customer_sub_group" text,
"credit_limit" numeric(15, 2) DEFAULT '0',
"payment_terms" text,
"notes" text,
"is_active" boolean DEFAULT true,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"created_by" uuid,
"updated_by" uuid,
"deleted_at" timestamp,
CONSTRAINT "ms_customers_crm_customer_code_unique" UNIQUE("crm_customer_code"),
CONSTRAINT "ms_customers_erp_customer_code_unique" UNIQUE("erp_customer_code")
);
--> statement-breakpoint
CREATE TABLE "document_sequences" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"document_type" text NOT NULL,
"prefix" text NOT NULL,
"period" text NOT NULL,
"current_number" integer DEFAULT 0 NOT NULL,
"padding_length" integer DEFAULT 3 NOT NULL,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"keycloak_id" text NOT NULL,
"email" text NOT NULL,
"name" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "users_keycloak_id_unique" UNIQUE("keycloak_id")
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_attachments" (
"id" serial PRIMARY KEY NOT NULL,
"quotation_id" integer NOT NULL,
"file_name" text NOT NULL,
"original_file_name" text NOT NULL,
"file_path" text NOT NULL,
"file_size" text NOT NULL,
"file_type" text NOT NULL,
"uploaded_at" timestamp DEFAULT now(),
"uploaded_by" text,
"description" text,
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_customers" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"quotation_id" uuid NOT NULL,
"customer_id" uuid NOT NULL,
"role" text NOT NULL,
"is_primary" boolean DEFAULT false,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"deleted_at" timestamp,
CONSTRAINT "uq_quotations_customer" UNIQUE("quotation_id","customer_id","role")
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_followups" (
"id" serial PRIMARY KEY NOT NULL,
"quotation_id" integer NOT NULL,
"followup_date" date NOT NULL,
"followup_type" text,
"contact_person" text,
"contact_method" text,
"outcome" text,
"notes" text,
"next_followup_date" date,
"next_action" text,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_items" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"quotation_id" uuid NOT NULL,
"item_number" text NOT NULL,
"product_type" text,
"description" text NOT NULL,
"quantity" numeric(10, 2) DEFAULT '1',
"unit" text DEFAULT 'pcs',
"unit_price" numeric(15, 2) DEFAULT '0',
"discount" numeric(15, 2) DEFAULT '0',
"discount_type" text DEFAULT 'percentage',
"tax_rate" numeric(5, 2) DEFAULT '7',
"total_price" numeric(15, 2) DEFAULT '0',
"notes" text,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "ms_quotations_template_mappings" (
"id" serial PRIMARY KEY NOT NULL,
"template_version_id" integer NOT NULL,
"placeholder_key" text NOT NULL,
"source_path" text NOT NULL,
"data_type" text NOT NULL,
"sheet_name" text,
"default_value" text,
"format_mask" text,
"sort_order" integer DEFAULT 0,
"created_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "ms_quotations_template_table_columns" (
"id" serial PRIMARY KEY NOT NULL,
"mapping_id" integer NOT NULL,
"column_name" text NOT NULL,
"source_field" text NOT NULL,
"column_letter" text,
"sort_order" integer NOT NULL,
"format_mask" text,
"created_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "ms_quotations_template_versions" (
"id" serial PRIMARY KEY NOT NULL,
"template_id" integer NOT NULL,
"version" text NOT NULL,
"file_path" text NOT NULL,
"is_active" boolean DEFAULT false,
"description" text,
"created_at" timestamp DEFAULT now(),
"created_by" text,
CONSTRAINT "uq_template_version" UNIQUE("template_id","version")
);
--> statement-breakpoint
CREATE TABLE "ms_quotations_templates" (
"id" serial PRIMARY KEY NOT NULL,
"product_type" text NOT NULL,
"file_type" text NOT NULL,
"template_name" text NOT NULL,
"template_path" text NOT NULL,
"is_default" boolean DEFAULT false,
"created_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "ms_quotations_topic_defaults" (
"id" serial PRIMARY KEY NOT NULL,
"product_type" text NOT NULL,
"topic_type" text NOT NULL,
"content" text NOT NULL,
"sort_order" integer DEFAULT 0,
"is_active" boolean DEFAULT true,
"created_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_topic_items" (
"id" serial PRIMARY KEY NOT NULL,
"topic_id" integer NOT NULL,
"content" text NOT NULL,
"sort_order" integer DEFAULT 0,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "tr_quotations_topics" (
"id" serial PRIMARY KEY NOT NULL,
"quotation_id" integer NOT NULL,
"topic_type" text NOT NULL,
"sort_order" integer DEFAULT 0,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "tr_quotations" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"branch_id" uuid NOT NULL,
"code" text NOT NULL,
"revision_no" integer DEFAULT 1 NOT NULL,
"parent_quotation_id" uuid,
"quotation_date" date NOT NULL,
"valid_until" date,
"quotation_type" text,
"competitor" text,
"mk_job_no" text,
"status" text DEFAULT 'draft',
"is_active" boolean DEFAULT true,
"revision" text DEFAULT '0',
"revision_remark" text,
"template_id" integer,
"subtotal" numeric(15, 2) DEFAULT '0',
"discount" numeric(15, 2) DEFAULT '0',
"discount_type" text DEFAULT 'percentage',
"tax_rate" numeric(5, 2) DEFAULT '7',
"tax_amount" numeric(15, 2) DEFAULT '0',
"total_amount" numeric(15, 2) DEFAULT '0',
"salesman_id" uuid,
"sale_admin_id" uuid,
"currency_code" text DEFAULT 'THB' NOT NULL,
"exchange_rate" numeric(12, 6) NOT NULL,
"base_currency_amount" numeric(15, 2),
"notes" text,
"reference" text,
"project" text,
"attention" text,
"location_province" text,
"location_industrial" text,
"location_orther" text,
"final_date" date,
"delivery_date" date,
"chance_percent" integer,
"is_hot_project" boolean DEFAULT false,
"approved_snapshot" jsonb,
"approved_pdf_url" text,
"is_sent" boolean DEFAULT false,
"sent_at" timestamp,
"sent_via" text,
"accepted_at" timestamp,
"rejected_at" timestamp,
"rejection_reason" text,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"created_by" uuid,
"updated_by" uuid,
"deleted_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "tr_quotation_contacts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"quotation_id" uuid NOT NULL,
"contact_id" uuid,
"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()
);
--> statement-breakpoint
CREATE TABLE "ms_industrial_estates" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"code" varchar(255) NOT NULL,
"name_th" varchar(255) NOT NULL,
"name_en" varchar(255),
"location_id" uuid NOT NULL,
"latitude" double precision,
"longitude" double precision,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
"created_by" text,
"updated_by" text
);
--> statement-breakpoint
CREATE TABLE "ms_locations" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"code" varchar(255) NOT NULL,
"name_th" varchar(255) NOT NULL,
"name_en" varchar(255),
"type" varchar(50) NOT NULL,
"parent_id" uuid,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "ms_options" (
"id" serial PRIMARY KEY NOT NULL,
"code" text NOT NULL,
"name" text NOT NULL,
"category" text NOT NULL,
"description" text,
"value" text,
"parent_id" integer,
"is_active" boolean DEFAULT true,
"sort_order" text DEFAULT '0',
"level" integer DEFAULT 0,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"created_by" text,
"updated_by" text,
"deleted_at" timestamp,
CONSTRAINT "ms_options_code_unique" UNIQUE("code")
);
--> statement-breakpoint
ALTER TABLE "ms_customer_contact_shares" ADD CONSTRAINT "ms_customer_contact_shares_contact_id_ms_customer_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."ms_customer_contacts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contact_shares" ADD CONSTRAINT "ms_customer_contact_shares_shared_with_user_id_users_id_fk" FOREIGN KEY ("shared_with_user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contact_shares" ADD CONSTRAINT "ms_customer_contact_shares_shared_by_users_id_fk" FOREIGN KEY ("shared_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contacts" ADD CONSTRAINT "ms_customer_contacts_branch_id_ms_branches_id_fk" FOREIGN KEY ("branch_id") REFERENCES "public"."ms_branches"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contacts" ADD CONSTRAINT "ms_customer_contacts_customer_id_ms_customers_id_fk" FOREIGN KEY ("customer_id") REFERENCES "public"."ms_customers"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contacts" ADD CONSTRAINT "ms_customer_contacts_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customer_contacts" ADD CONSTRAINT "ms_customer_contacts_updated_by_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customers" ADD CONSTRAINT "ms_customers_branch_id_ms_branches_id_fk" FOREIGN KEY ("branch_id") REFERENCES "public"."ms_branches"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customers" ADD CONSTRAINT "ms_customers_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_customers" ADD CONSTRAINT "ms_customers_updated_by_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_attachments" ADD CONSTRAINT "tr_quotations_attachments_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_customers" ADD CONSTRAINT "tr_quotations_customers_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_customers" ADD CONSTRAINT "tr_quotations_customers_customer_id_ms_customers_id_fk" FOREIGN KEY ("customer_id") REFERENCES "public"."ms_customers"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_followups" ADD CONSTRAINT "tr_quotations_followups_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_items" ADD CONSTRAINT "tr_quotations_items_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_mappings" ADD CONSTRAINT "ms_quotations_template_mappings_template_version_id_ms_quotations_template_versions_id_fk" FOREIGN KEY ("template_version_id") REFERENCES "public"."ms_quotations_template_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_table_columns" ADD CONSTRAINT "ms_quotations_template_table_columns_mapping_id_ms_quotations_template_mappings_id_fk" FOREIGN KEY ("mapping_id") REFERENCES "public"."ms_quotations_template_mappings"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_versions" ADD CONSTRAINT "ms_quotations_template_versions_template_id_ms_quotations_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."ms_quotations_templates"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_topic_items" ADD CONSTRAINT "tr_quotations_topic_items_topic_id_tr_quotations_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."tr_quotations_topics"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations_topics" ADD CONSTRAINT "tr_quotations_topics_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_branch_id_ms_branches_id_fk" FOREIGN KEY ("branch_id") REFERENCES "public"."ms_branches"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_parent_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("parent_quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_salesman_id_users_id_fk" FOREIGN KEY ("salesman_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_sale_admin_id_users_id_fk" FOREIGN KEY ("sale_admin_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotations" ADD CONSTRAINT "tr_quotations_updated_by_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotation_contacts" ADD CONSTRAINT "tr_quotation_contacts_quotation_id_tr_quotations_id_fk" FOREIGN KEY ("quotation_id") REFERENCES "public"."tr_quotations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "tr_quotation_contacts" ADD CONSTRAINT "tr_quotation_contacts_contact_id_ms_customer_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."ms_customer_contacts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ms_industrial_estates" ADD CONSTRAINT "ms_industrial_estates_location_id_ms_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."ms_locations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_branches_code" ON "ms_branches" USING btree ("code");--> statement-breakpoint
CREATE INDEX "idx_branches_is_active" ON "ms_branches" USING btree ("is_active");--> statement-breakpoint
CREATE INDEX "idx_contact_shares_contact" ON "ms_customer_contact_shares" USING btree ("contact_id");--> statement-breakpoint
CREATE INDEX "idx_contact_shares_user" ON "ms_customer_contact_shares" USING btree ("shared_with_user_id");--> statement-breakpoint
CREATE INDEX "idx_contact_shares_shared_by" ON "ms_customer_contact_shares" USING btree ("shared_by");--> statement-breakpoint
CREATE INDEX "idx_contacts_customer" ON "ms_customer_contacts" USING btree ("customer_id");--> statement-breakpoint
CREATE INDEX "idx_contacts_branch" ON "ms_customer_contacts" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "idx_contacts_created_by" ON "ms_customer_contacts" USING btree ("created_by");--> statement-breakpoint
CREATE INDEX "idx_contacts_visibility" ON "ms_customer_contacts" USING btree ("customer_id","created_by");--> statement-breakpoint
CREATE INDEX "idx_customers_branch" ON "ms_customers" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "idx_customers_status" ON "ms_customers" USING btree ("customer_status");--> statement-breakpoint
CREATE INDEX "idx_customers_crm_code" ON "ms_customers" USING btree ("crm_customer_code");--> statement-breakpoint
CREATE INDEX "idx_customers_erp_code" ON "ms_customers" USING btree ("erp_customer_code");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_document_period" ON "document_sequences" USING btree ("document_type","period");--> statement-breakpoint
CREATE INDEX "idx_qcust_quotation_id" ON "tr_quotations_customers" USING btree ("quotation_id");--> statement-breakpoint
CREATE INDEX "idx_qcust_customer_id" ON "tr_quotations_customers" USING btree ("customer_id");--> statement-breakpoint
CREATE INDEX "idx_qitem_quotation_id" ON "tr_quotations_items" USING btree ("quotation_id");--> statement-breakpoint
CREATE INDEX "idx_mapping_template_version" ON "ms_quotations_template_mappings" USING btree ("template_version_id");--> statement-breakpoint
CREATE INDEX "idx_mapping_placeholder" ON "ms_quotations_template_mappings" USING btree ("placeholder_key");--> statement-breakpoint
CREATE INDEX "idx_tablecol_mapping" ON "ms_quotations_template_table_columns" USING btree ("mapping_id");--> statement-breakpoint
CREATE INDEX "idx_quotations_branch" ON "tr_quotations" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "idx_quotations_code" ON "tr_quotations" USING btree ("code");--> statement-breakpoint
CREATE INDEX "idx_quotation_status" ON "tr_quotations" USING btree ("status");--> statement-breakpoint
CREATE INDEX "idx_quotation_date" ON "tr_quotations" USING btree ("quotation_date");--> statement-breakpoint
CREATE INDEX "idx_quotations_branch_status" ON "tr_quotations" USING btree ("branch_id","status");--> statement-breakpoint
CREATE INDEX "idx_quotations_revision" ON "tr_quotations" USING btree ("parent_quotation_id");--> statement-breakpoint
CREATE INDEX "idx_quotation_contact" ON "tr_quotation_contacts" USING btree ("quotation_id");--> statement-breakpoint
CREATE INDEX "idx_quotation_contact_contact" ON "tr_quotation_contacts" USING btree ("contact_id");--> statement-breakpoint
CREATE INDEX "location_id_idx" ON "ms_industrial_estates" USING btree ("location_id");--> statement-breakpoint
CREATE INDEX "type_idx" ON "ms_locations" USING btree ("type");--> statement-breakpoint
CREATE INDEX "parent_id_idx" ON "ms_locations" USING btree ("parent_id");

View File

@@ -0,0 +1,54 @@
ALTER TABLE "ms_options" DROP CONSTRAINT "ms_options_code_unique";--> statement-breakpoint
DROP INDEX "location_id_idx";--> statement-breakpoint
DROP INDEX "type_idx";--> statement-breakpoint
DROP INDEX "parent_id_idx";--> statement-breakpoint
ALTER TABLE "tr_audit_logs" ALTER COLUMN "before_data" SET DATA TYPE jsonb;--> statement-breakpoint
ALTER TABLE "tr_audit_logs" ALTER COLUMN "after_data" SET DATA TYPE jsonb;--> statement-breakpoint
ALTER TABLE "tr_quotations_attachments" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_attachments" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "tr_quotations_attachments" ALTER COLUMN "quotation_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_followups" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_followups" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "tr_quotations_followups" ALTER COLUMN "quotation_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_mappings" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_mappings" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "ms_quotations_template_mappings" ALTER COLUMN "template_version_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_table_columns" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_table_columns" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "ms_quotations_template_table_columns" ALTER COLUMN "mapping_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_versions" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_quotations_template_versions" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "ms_quotations_template_versions" ALTER COLUMN "template_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_topic_items" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_topic_items" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "tr_quotations_topic_items" ALTER COLUMN "topic_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_topics" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "tr_quotations_topics" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();--> statement-breakpoint
ALTER TABLE "tr_quotations_topics" ALTER COLUMN "quotation_id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "ms_options" ALTER COLUMN "sort_order" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "tr_audit_logs" ADD COLUMN "branch_id" text;--> statement-breakpoint
ALTER TABLE "tr_audit_logs" ADD COLUMN "user_id" text;--> statement-breakpoint
ALTER TABLE "tr_audit_logs" ADD COLUMN "action_type" text;--> statement-breakpoint
ALTER TABLE "ms_industrial_estates" ADD COLUMN "branch_id" text NOT NULL;--> statement-breakpoint
ALTER TABLE "ms_industrial_estates" ADD COLUMN "is_active" boolean DEFAULT true;--> statement-breakpoint
ALTER TABLE "ms_locations" ADD COLUMN "branch_id" text NOT NULL;--> statement-breakpoint
ALTER TABLE "ms_options" ADD COLUMN "branch_id" text NOT NULL;--> statement-breakpoint
CREATE INDEX "tr_audit_logs_branch_id_idx" ON "tr_audit_logs" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_user_id_idx" ON "tr_audit_logs" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_entity_type_idx" ON "tr_audit_logs" USING btree ("entity_type");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_entity_id_idx" ON "tr_audit_logs" USING btree ("entity_id");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_action_idx" ON "tr_audit_logs" USING btree ("action");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_created_at_idx" ON "tr_audit_logs" USING btree ("created_at");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_branch_entity_idx" ON "tr_audit_logs" USING btree ("branch_id","entity_type");--> statement-breakpoint
CREATE INDEX "tr_audit_logs_user_entity_idx" ON "tr_audit_logs" USING btree ("user_id","entity_type");--> statement-breakpoint
CREATE INDEX "ms_industrial_estates_branch_id_idx" ON "ms_industrial_estates" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "ms_industrial_estates_location_id_idx" ON "ms_industrial_estates" USING btree ("location_id");--> statement-breakpoint
CREATE INDEX "ms_industrial_estates_branch_location_idx" ON "ms_industrial_estates" USING btree ("branch_id","location_id");--> statement-breakpoint
CREATE INDEX "ms_locations_branch_id_idx" ON "ms_locations" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "ms_locations_type_idx" ON "ms_locations" USING btree ("type");--> statement-breakpoint
CREATE INDEX "ms_locations_parent_id_idx" ON "ms_locations" USING btree ("parent_id");--> statement-breakpoint
CREATE INDEX "ms_locations_branch_type_idx" ON "ms_locations" USING btree ("branch_id","type");--> statement-breakpoint
CREATE INDEX "ms_options_branch_id_idx" ON "ms_options" USING btree ("branch_id");--> statement-breakpoint
CREATE INDEX "ms_options_category_idx" ON "ms_options" USING btree ("category");--> statement-breakpoint
CREATE INDEX "ms_options_branch_category_idx" ON "ms_options" USING btree ("branch_id","category");--> statement-breakpoint
CREATE INDEX "ms_options_code_idx" ON "ms_options" USING btree ("code");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1776955974943,
"tag": "0000_cultured_dreaming_celestial",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1777132888826,
"tag": "0001_curvy_sunspot",
"breakpoints": true
}
]
}

View File

@@ -0,0 +1,590 @@
-- ============================================================
-- 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;