getDriverName() !== 'pgsql') { throw new RuntimeException('InvestMoz requires PostgreSQL with PostGIS. Use the isolated pgsql test environment.'); } DB::statement(<<<'SQL' CREATE TABLE public.outbox_events ( 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(), aggregate_type text NOT NULL, aggregate_id uuid NOT NULL, event_type text NOT NULL, payload jsonb NOT NULL, status text NOT NULL, available_at timestamptz NOT NULL DEFAULT now(), published_at timestamptz, CONSTRAINT imz_outbox_events_pk PRIMARY KEY (id), CONSTRAINT imz_outbox_events_check_01 CHECK (version_no > 0), CONSTRAINT imz_outbox_events_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_outbox_events_check_03 CHECK (status IN ('pending','published','failed')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.inbox_events ( 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(), message_id text NOT NULL, event_type text NOT NULL, payload jsonb NOT NULL, status text NOT NULL, received_at timestamptz NOT NULL DEFAULT now(), processed_at timestamptz, CONSTRAINT imz_inbox_events_pk PRIMARY KEY (id), CONSTRAINT imz_inbox_events_check_01 CHECK (version_no > 0), CONSTRAINT imz_inbox_events_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_inbox_events_check_03 CHECK (status IN ('received','processed','failed')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.sync_runs ( 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, integration_name text NOT NULL, direction text NOT NULL, status text NOT NULL, started_at timestamptz NOT NULL DEFAULT now(), ended_at timestamptz, totals jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT imz_sync_runs_pk PRIMARY KEY (id), CONSTRAINT imz_sync_runs_check_01 CHECK (version_no > 0), CONSTRAINT imz_sync_runs_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_sync_runs_check_03 CHECK (direction IN ('inbound','outbound')), CONSTRAINT imz_sync_runs_check_04 CHECK (status IN ('running','succeeded','failed','cancelled')) ); SQL); DB::statement(<<<'SQL' CREATE TABLE public.reconciliation_issues ( 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, sync_run_id uuid NOT NULL CONSTRAINT imz_reconciliation_issues_sync_run_id_fk REFERENCES public.sync_runs(id) ON DELETE CASCADE ON UPDATE CASCADE, entity_type text NOT NULL, external_id_ref text, issue_type text NOT NULL, severity text NOT NULL, status text NOT NULL, details jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT imz_reconciliation_issues_pk PRIMARY KEY (id), CONSTRAINT imz_reconciliation_issues_check_01 CHECK (version_no > 0), CONSTRAINT imz_reconciliation_issues_check_02 CHECK (data_classification IN ('public','internal','confidential','restricted')), CONSTRAINT imz_reconciliation_issues_check_03 CHECK (issue_type IN ('missing','duplicate','orphan','checksum','geometry','value')), CONSTRAINT imz_reconciliation_issues_check_04 CHECK (severity IN ('warning','error')), CONSTRAINT imz_reconciliation_issues_check_05 CHECK (status IN ('open','accepted','resolved')) ); 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.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_sync_run_id_fk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_check_04 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.reconciliation_issues DROP CONSTRAINT IF EXISTS imz_reconciliation_issues_check_05 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.reconciliation_issues RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.sync_runs DROP CONSTRAINT IF EXISTS imz_sync_runs_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.sync_runs DROP CONSTRAINT IF EXISTS imz_sync_runs_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.sync_runs DROP CONSTRAINT IF EXISTS imz_sync_runs_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.sync_runs DROP CONSTRAINT IF EXISTS imz_sync_runs_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.sync_runs DROP CONSTRAINT IF EXISTS imz_sync_runs_check_04 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.sync_runs RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.inbox_events DROP CONSTRAINT IF EXISTS imz_inbox_events_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.inbox_events DROP CONSTRAINT IF EXISTS imz_inbox_events_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.inbox_events DROP CONSTRAINT IF EXISTS imz_inbox_events_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.inbox_events DROP CONSTRAINT IF EXISTS imz_inbox_events_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.inbox_events RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.outbox_events DROP CONSTRAINT IF EXISTS imz_outbox_events_pk RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.outbox_events DROP CONSTRAINT IF EXISTS imz_outbox_events_check_01 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.outbox_events DROP CONSTRAINT IF EXISTS imz_outbox_events_check_02 RESTRICT; SQL); DB::statement(<<<'SQL' ALTER TABLE IF EXISTS public.outbox_events DROP CONSTRAINT IF EXISTS imz_outbox_events_check_03 RESTRICT; SQL); DB::statement(<<<'SQL' DROP TABLE IF EXISTS public.outbox_events RESTRICT; SQL); } };