CREATE TABLE IF NOT EXISTS tenants ( id text PRIMARY KEY, name text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS sites ( id uuid PRIMARY KEY, tenant_id text NOT NULL REFERENCES tenants(id), name text NOT NULL, timezone text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS stations ( id uuid PRIMARY KEY, site_id uuid NOT NULL REFERENCES sites(id), name text NOT NULL, station_type text NOT NULL, active boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS devices ( id uuid PRIMARY KEY, station_id uuid NOT NULL REFERENCES stations(id), installation_id uuid NOT NULL UNIQUE, label text, last_seen_at timestamptz, app_version text, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS scans ( id uuid PRIMARY KEY, tenant_id text NOT NULL REFERENCES tenants(id), site_id uuid NOT NULL REFERENCES sites(id), station_id uuid NOT NULL REFERENCES stations(id), device_id uuid NOT NULL REFERENCES devices(id), operator_subject text NOT NULL, raw_value text NOT NULL, format text NOT NULL, duplicate boolean NOT NULL DEFAULT false, captured_at timestamptz NOT NULL, received_at timestamptz NOT NULL DEFAULT now(), schema_version integer NOT NULL ); CREATE TABLE IF NOT EXISTS pallet_sessions ( id uuid PRIMARY KEY, tenant_id text NOT NULL REFERENCES tenants(id), site_id uuid NOT NULL REFERENCES sites(id), station_id uuid NOT NULL REFERENCES stations(id), device_id uuid NOT NULL REFERENCES devices(id), operator_subject text NOT NULL, workflow_type text NOT NULL, target_count integer NOT NULL, started_at timestamptz NOT NULL, completed_at timestamptz, status text NOT NULL, conforms boolean, result_status text, schema_version integer NOT NULL DEFAULT 1, received_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS pallet_observations ( id uuid PRIMARY KEY, pallet_session_id uuid NOT NULL REFERENCES pallet_sessions(id) ON DELETE CASCADE, carton_index integer NOT NULL, ean text, fields_json jsonb, raw_text text, confidence double precision, captured_at timestamptz NOT NULL, schema_version integer NOT NULL DEFAULT 1, received_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS pallet_exceptions ( id uuid PRIMARY KEY, pallet_session_id uuid NOT NULL REFERENCES pallet_sessions(id) ON DELETE CASCADE, observation_id uuid REFERENCES pallet_observations(id) ON DELETE SET NULL, field text NOT NULL, expected_value text, actual_value text, exception_type text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS sync_events ( id uuid PRIMARY KEY, aggregate_id uuid NOT NULL, event_type text NOT NULL, schema_version integer NOT NULL, payload jsonb NOT NULL, status text NOT NULL, received_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS reference_label_specs ( id uuid PRIMARY KEY, tenant_id text NOT NULL REFERENCES tenants(id), ean text NOT NULL, fields_json jsonb NOT NULL, active boolean NOT NULL DEFAULT true, updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_sites_tenant ON sites(tenant_id); CREATE INDEX IF NOT EXISTS idx_stations_site ON stations(site_id); CREATE INDEX IF NOT EXISTS idx_devices_station ON devices(station_id); CREATE INDEX IF NOT EXISTS idx_devices_last_seen ON devices(last_seen_at DESC); CREATE INDEX IF NOT EXISTS idx_scans_tenant_captured ON scans(tenant_id, captured_at DESC); CREATE INDEX IF NOT EXISTS idx_scans_station_captured ON scans(station_id, captured_at DESC); CREATE INDEX IF NOT EXISTS idx_scans_device_captured ON scans(device_id, captured_at DESC); CREATE INDEX IF NOT EXISTS idx_pallet_sessions_tenant_started ON pallet_sessions(tenant_id, started_at DESC); CREATE INDEX IF NOT EXISTS idx_pallet_sessions_station_started ON pallet_sessions(station_id, started_at DESC); CREATE INDEX IF NOT EXISTS idx_pallet_observations_session ON pallet_observations(pallet_session_id, carton_index); CREATE INDEX IF NOT EXISTS idx_pallet_exceptions_session ON pallet_exceptions(pallet_session_id); CREATE INDEX IF NOT EXISTS idx_sync_events_aggregate ON sync_events(aggregate_id); CREATE INDEX IF NOT EXISTS idx_reference_label_specs_ean ON reference_label_specs(tenant_id, ean) WHERE active = true;