getDriverName() !== 'pgsql') { throw new RuntimeException('InvestMoz requires PostgreSQL with PostGIS. Use the isolated pgsql test environment.'); } DB::statement(<<<'SQL' CREATE TABLE public.economic_clusters ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, code text NOT NULL, name text NOT NULL, sector text NOT NULL, status text NOT NULL, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT imz_economic_clusters_pk PRIMARY KEY (id), CONSTRAINT imz_economic_clusters_check_01 CHECK (version_no > 0), CONSTRAINT imz_economic_clusters_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_economic_clusters_check_03 CHECK (status IN ('planned','active','inactive')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.value_chains ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, cluster_id uuid NOT NULL CONSTRAINT imz_value_chains_cluster_id_fk REFERENCES public.economic_clusters(id) ON DELETE CASCADE ON UPDATE CASCADE, code text NOT NULL, name text NOT NULL, stage text NOT NULL, CONSTRAINT imz_value_chains_pk PRIMARY KEY (id), CONSTRAINT imz_value_chains_check_01 CHECK (version_no > 0), CONSTRAINT imz_value_chains_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_value_chains_check_03 CHECK (stage IN ('input','production','processing','logistics','market')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.value_chain_links ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, from_value_chain_id uuid NOT NULL CONSTRAINT imz_value_chain_links_from_value_chain_id_fk REFERENCES public.value_chains(id) ON DELETE CASCADE ON UPDATE CASCADE, to_value_chain_id uuid NOT NULL CONSTRAINT imz_value_chain_links_to_value_chain_id_fk REFERENCES public.value_chains(id) ON DELETE CASCADE ON UPDATE CASCADE, link_type text NOT NULL, CONSTRAINT imz_value_chain_links_pk PRIMARY KEY (id), CONSTRAINT imz_value_chain_links_check_01 CHECK (version_no > 0), CONSTRAINT imz_value_chain_links_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_value_chain_links_check_03 CHECK (link_type IN ('supplies','processes','distributes','supports')), CONSTRAINT imz_value_chain_links_check_04 CHECK (from_value_chain_id <> to_value_chain_id) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.cluster_projects ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, cluster_id uuid NOT NULL CONSTRAINT imz_cluster_projects_cluster_id_fk REFERENCES public.economic_clusters(id) ON DELETE CASCADE ON UPDATE CASCADE, project_id uuid NOT NULL CONSTRAINT imz_cluster_projects_project_id_fk REFERENCES public.projects(id) ON DELETE CASCADE ON UPDATE CASCADE, role text NOT NULL, CONSTRAINT imz_cluster_projects_pk PRIMARY KEY (id), CONSTRAINT imz_cluster_projects_check_01 CHECK (version_no > 0), CONSTRAINT imz_cluster_projects_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_cluster_projects_check_03 CHECK (role IN ('anchor','complementary','supporting')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.anchor_project_links ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, anchor_project_id uuid NOT NULL CONSTRAINT imz_anchor_project_links_anchor_project_id_fk REFERENCES public.projects(id) ON DELETE CASCADE ON UPDATE CASCADE, cluster_id uuid NOT NULL CONSTRAINT imz_anchor_project_links_cluster_id_fk REFERENCES public.economic_clusters(id) ON DELETE CASCADE ON UPDATE CASCADE, rationale text NOT NULL, CONSTRAINT imz_anchor_project_links_pk PRIMARY KEY (id), CONSTRAINT imz_anchor_project_links_check_01 CHECK (version_no > 0), CONSTRAINT imz_anchor_project_links_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.complementary_project_links ( id uuid DEFAULT gen_random_uuid() NOT NULL, source_system text NOT NULL DEFAULT 'investmoz', external_id text, version_no integer NOT NULL DEFAULT 1, data_classification text NOT NULL DEFAULT 'internal', correlation_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, anchor_project_id uuid NOT NULL CONSTRAINT imz_complementary_project_links_anchor_project_id_fk REFERENCES public.projects(id) ON DELETE CASCADE ON UPDATE CASCADE, complementary_project_id uuid NOT NULL CONSTRAINT imz_complementary_project_links_complementary_project_id_fk REFERENCES public.projects(id) ON DELETE CASCADE ON UPDATE CASCADE, relation_type text NOT NULL, CONSTRAINT imz_complementary_project_links_pk PRIMARY KEY (id), CONSTRAINT imz_complementary_project_links_check_01 CHECK (version_no > 0), CONSTRAINT imz_complementary_project_links_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_complementary_project_links_check_03 CHECK (relation_type IN ('dependency','enabler','supplier','market')), CONSTRAINT imz_complementary_project_links_check_04 CHECK (anchor_project_id<>complementary_project_id) ); SQL); } public function down(): void { if (DB::connection()->getDriverName() !== 'pgsql') { throw new RuntimeException('InvestMoz rollback requires PostgreSQL.'); } DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_anchor_project_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_complementary_project_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.complementary_project_links DROP CONSTRAINT IF EXISTS imz_complementary_project_links_check_04 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.complementary_project_links RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.anchor_project_links DROP CONSTRAINT IF EXISTS imz_anchor_project_links_anchor_project_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.anchor_project_links DROP CONSTRAINT IF EXISTS imz_anchor_project_links_cluster_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.anchor_project_links DROP CONSTRAINT IF EXISTS imz_anchor_project_links_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.anchor_project_links DROP CONSTRAINT IF EXISTS imz_anchor_project_links_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.anchor_project_links DROP CONSTRAINT IF EXISTS imz_anchor_project_links_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.anchor_project_links RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_cluster_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_project_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.cluster_projects DROP CONSTRAINT IF EXISTS imz_cluster_projects_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.cluster_projects RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_from_value_chain_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_to_value_chain_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chain_links DROP CONSTRAINT IF EXISTS imz_value_chain_links_check_04 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.value_chain_links RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chains DROP CONSTRAINT IF EXISTS imz_value_chains_cluster_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chains DROP CONSTRAINT IF EXISTS imz_value_chains_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chains DROP CONSTRAINT IF EXISTS imz_value_chains_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chains DROP CONSTRAINT IF EXISTS imz_value_chains_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.value_chains DROP CONSTRAINT IF EXISTS imz_value_chains_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.value_chains RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.economic_clusters DROP CONSTRAINT IF EXISTS imz_economic_clusters_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.economic_clusters DROP CONSTRAINT IF EXISTS imz_economic_clusters_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.economic_clusters DROP CONSTRAINT IF EXISTS imz_economic_clusters_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.economic_clusters DROP CONSTRAINT IF EXISTS imz_economic_clusters_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.economic_clusters RESTRICT; SQL); } };