-- Sankofa HW Infra initial schema CREATE TABLE IF NOT EXISTS "org_units" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "name" text NOT NULL, "parent_id" uuid, "org_id" text NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE TABLE IF NOT EXISTS "users" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "email" text NOT NULL UNIQUE, "name" text, "org_unit_id" uuid, "org_id" text NOT NULL, "external_id" text, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "users" ADD CONSTRAINT "users_org_unit_id_org_units_id_fk" FOREIGN KEY ("org_unit_id") REFERENCES "public"."org_units"("id"); ALTER TABLE "org_units" ADD CONSTRAINT "org_units_parent_id_org_units_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."org_units"("id"); CREATE TABLE IF NOT EXISTS "vendors" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "legal_name" text NOT NULL, "contacts" jsonb, "trust_tier" text DEFAULT 'unknown' NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE TABLE IF NOT EXISTS "vendor_bank_details" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "vendor_id" uuid NOT NULL, "version" integer DEFAULT 1 NOT NULL, "instructions" jsonb, "approved_by_1" uuid, "approved_by_2" uuid, "approved_at" timestamp, "created_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "vendor_bank_details" ADD CONSTRAINT "vendor_bank_details_vendor_id_vendors_id_fk" FOREIGN KEY ("vendor_id") REFERENCES "public"."vendors"("id"); CREATE TABLE IF NOT EXISTS "offers" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "vendor_id" uuid NOT NULL, "sku" text, "mpn" text, "quantity" integer NOT NULL, "unit_price" decimal(20, 4) NOT NULL, "incoterms" text, "lead_time_days" integer, "country_of_origin" text, "condition" text, "warranty" text, "evidence_refs" jsonb, "risk_score" decimal(5, 2), "risk_factors" jsonb, "status" text DEFAULT 'draft' NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "offers_vendor_idx" ON "offers" ("vendor_id"); CREATE INDEX IF NOT EXISTS "offers_org_idx" ON "offers" ("org_id"); ALTER TABLE "offers" ADD CONSTRAINT "offers_vendor_id_vendors_id_fk" FOREIGN KEY ("vendor_id") REFERENCES "public"."vendors"("id"); CREATE TABLE IF NOT EXISTS "regions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "name" text NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE TABLE IF NOT EXISTS "sites" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "region_id" uuid, "name" text NOT NULL, "address" text, "network_metadata" jsonb, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "sites_org_idx" ON "sites" ("org_id"); ALTER TABLE "sites" ADD CONSTRAINT "sites_region_id_regions_id_fk" FOREIGN KEY ("region_id") REFERENCES "public"."regions"("id"); CREATE TABLE IF NOT EXISTS "rooms" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "name" text NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "rooms" ADD CONSTRAINT "rooms_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id"); CREATE TABLE IF NOT EXISTS "rows" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "room_id" uuid NOT NULL, "name" text NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "rows" ADD CONSTRAINT "rows_room_id_rooms_id_fk" FOREIGN KEY ("room_id") REFERENCES "public"."rooms"("id"); CREATE TABLE IF NOT EXISTS "racks" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "row_id" uuid NOT NULL, "name" text NOT NULL, "ru_total" integer NOT NULL, "power_feeds" jsonb, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "racks_row_idx" ON "racks" ("row_id"); ALTER TABLE "racks" ADD CONSTRAINT "racks_row_id_rows_id_fk" FOREIGN KEY ("row_id") REFERENCES "public"."rows"("id"); CREATE TABLE IF NOT EXISTS "assets" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "asset_id" text NOT NULL UNIQUE, "category" text NOT NULL, "manufacturer_serial" text, "service_tag" text, "macs" jsonb, "wwns" jsonb, "part_number" text, "condition" text, "warranty" text, "proof_artifact_refs" jsonb, "site_id" uuid, "position_id" uuid, "owner_id" uuid, "project_id" text, "sensitivity_tier" text, "status" text DEFAULT 'pending' NOT NULL, "chain_of_custody" jsonb, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "assets_org_idx" ON "assets" ("org_id"); CREATE INDEX IF NOT EXISTS "assets_site_idx" ON "assets" ("site_id"); CREATE INDEX IF NOT EXISTS "assets_category_idx" ON "assets" ("category"); CREATE INDEX IF NOT EXISTS "assets_status_idx" ON "assets" ("status"); ALTER TABLE "assets" ADD CONSTRAINT "assets_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id"); ALTER TABLE "assets" ADD CONSTRAINT "assets_owner_id_users_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."users"("id"); CREATE TABLE IF NOT EXISTS "positions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "rack_id" uuid NOT NULL, "ru_start" integer NOT NULL, "ru_end" integer NOT NULL, "asset_id" uuid, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "positions_rack_idx" ON "positions" ("rack_id"); ALTER TABLE "positions" ADD CONSTRAINT "positions_rack_id_racks_id_fk" FOREIGN KEY ("rack_id") REFERENCES "public"."racks"("id"); ALTER TABLE "positions" ADD CONSTRAINT "positions_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id"); ALTER TABLE "assets" ADD CONSTRAINT "assets_position_id_positions_id_fk" FOREIGN KEY ("position_id") REFERENCES "public"."positions"("id"); CREATE TABLE IF NOT EXISTS "purchase_orders" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "vendor_id" uuid NOT NULL, "line_items" jsonb NOT NULL, "status" text DEFAULT 'draft' NOT NULL, "approval_stage" text, "escrow_terms" text, "inspection_site_id" uuid, "delivery_site_id" uuid, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "po_vendor_idx" ON "purchase_orders" ("vendor_id"); CREATE INDEX IF NOT EXISTS "po_org_idx" ON "purchase_orders" ("org_id"); ALTER TABLE "purchase_orders" ADD CONSTRAINT "purchase_orders_vendor_id_vendors_id_fk" FOREIGN KEY ("vendor_id") REFERENCES "public"."vendors"("id"); ALTER TABLE "purchase_orders" ADD CONSTRAINT "purchase_orders_inspection_site_id_sites_id_fk" FOREIGN KEY ("inspection_site_id") REFERENCES "public"."sites"("id"); ALTER TABLE "purchase_orders" ADD CONSTRAINT "purchase_orders_delivery_site_id_sites_id_fk" FOREIGN KEY ("delivery_site_id") REFERENCES "public"."sites"("id"); CREATE TABLE IF NOT EXISTS "shipments" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "purchase_order_id" uuid NOT NULL, "tracking" text, "cartons_pallets" jsonb, "customs_docs_refs" jsonb, "status" text DEFAULT 'pending' NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "shipments" ADD CONSTRAINT "shipments_purchase_order_id_purchase_orders_id_fk" FOREIGN KEY ("purchase_order_id") REFERENCES "public"."purchase_orders"("id"); CREATE TABLE IF NOT EXISTS "asset_components" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "parent_asset_id" uuid NOT NULL, "child_asset_id" uuid NOT NULL, "role" text NOT NULL, "slot_index" integer, "created_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "asset_components_parent_idx" ON "asset_components" ("parent_asset_id"); CREATE INDEX IF NOT EXISTS "asset_components_child_idx" ON "asset_components" ("child_asset_id"); ALTER TABLE "asset_components" ADD CONSTRAINT "asset_components_parent_asset_id_assets_id_fk" FOREIGN KEY ("parent_asset_id") REFERENCES "public"."assets"("id"); ALTER TABLE "asset_components" ADD CONSTRAINT "asset_components_child_asset_id_assets_id_fk" FOREIGN KEY ("child_asset_id") REFERENCES "public"."assets"("id"); CREATE TABLE IF NOT EXISTS "provisioning_records" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "asset_id" uuid NOT NULL, "os_image" text, "hypervisor_node" text, "cluster_id" text, "metadata" jsonb, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "provisioning_records" ADD CONSTRAINT "provisioning_records_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id"); CREATE TABLE IF NOT EXISTS "maintenances" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "asset_id" uuid NOT NULL, "type" text NOT NULL, "vendor_ticket_ref" text, "description" text, "status" text DEFAULT 'open' NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "maintenances" ADD CONSTRAINT "maintenances_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id"); CREATE TABLE IF NOT EXISTS "audit_events" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "org_id" text NOT NULL, "actor_id" uuid, "actor_email" text, "action" text NOT NULL, "resource_type" text NOT NULL, "resource_id" text NOT NULL, "before_state" jsonb, "after_state" jsonb, "occurred_at" timestamp DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS "audit_events_org_idx" ON "audit_events" ("org_id"); CREATE INDEX IF NOT EXISTS "audit_events_resource_idx" ON "audit_events" ("resource_type", "resource_id"); CREATE INDEX IF NOT EXISTS "audit_events_occurred_idx" ON "audit_events" ("occurred_at"); ALTER TABLE "audit_events" ADD CONSTRAINT "audit_events_actor_id_users_id_fk" FOREIGN KEY ("actor_id") REFERENCES "public"."users"("id"); CREATE TABLE IF NOT EXISTS "roles" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "name" text NOT NULL UNIQUE, "description" text, "permissions" jsonb DEFAULT '[]' NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, "updated_at" timestamp DEFAULT now() NOT NULL ); CREATE TABLE IF NOT EXISTS "user_roles" ( "user_id" uuid NOT NULL, "role_id" uuid NOT NULL, "scope_site_id" uuid, "scope_project_id" text, "assigned_at" timestamp DEFAULT now() NOT NULL ); ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id"); ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_id_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("id"); ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_scope_site_id_sites_id_fk" FOREIGN KEY ("scope_site_id") REFERENCES "public"."sites"("id"); ALTER TABLE "user_roles" ADD PRIMARY KEY ("user_id", "role_id");