-- Migration 099: Flexoptix Internal Demand Data Schema -- ⚠️ SECURITY: This table contains proprietary Flexoptix business intelligence. -- Raw records MUST NEVER be exposed via any public-facing API endpoint. -- Use ONLY for aggregated forecasting and hype cycle calibration. -- Data source: internal XLSX export (AES-256-CBC encrypted at rest on local infra) -- -- velocity_class breakdown: -- fast_mover — demand_12m >= 100 units/month -- regular — demand_12m >= 10 and < 100 -- slow_mover — demand_12m > 0 and < 10 -- dead_stock — demand_12m = 0 CREATE TABLE IF NOT EXISTS flexoptix_internal_demand ( id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY, sku TEXT NOT NULL, -- Artikel-Nr (e.g. AT.L.1BU) description TEXT, -- Kurzbeschreibung transceiver_id UUID REFERENCES transceivers(id) ON DELETE SET NULL, demand_12m NUMERIC(12,2) NOT NULL DEFAULT 0, -- Bedarf/Monat letzte 12 Monate demand_3m NUMERIC(12,2) NOT NULL DEFAULT 0, -- Bedarf/Monat letzte 3 Monate demand_trend_pct NUMERIC(8,2), -- (3m-12m)/12m*100 (+= accelerating) velocity_class TEXT CHECK (velocity_class IN ('fast_mover','regular','slow_mover','dead_stock')), imported_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), data_source TEXT NOT NULL DEFAULT 'flexoptix_internal', is_internal BOOLEAN NOT NULL DEFAULT TRUE, -- security guard — always TRUE CONSTRAINT flexoptix_demand_sku_unique UNIQUE (sku) ); COMMENT ON TABLE flexoptix_internal_demand IS 'INTERNAL — Flexoptix proprietary sales velocity data. Never expose raw rows publicly.'; -- Indexes CREATE INDEX IF NOT EXISTS idx_foxd_sku ON flexoptix_internal_demand (sku); CREATE INDEX IF NOT EXISTS idx_foxd_transceiver ON flexoptix_internal_demand (transceiver_id) WHERE transceiver_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_foxd_velocity ON flexoptix_internal_demand (velocity_class); CREATE INDEX IF NOT EXISTS idx_foxd_demand_12m ON flexoptix_internal_demand (demand_12m DESC); CREATE INDEX IF NOT EXISTS idx_foxd_is_internal ON flexoptix_internal_demand (is_internal); -- Row-level security: prevent accidental exposure -- (API layer enforces localhost-only on top of this) ALTER TABLE flexoptix_internal_demand ENABLE ROW LEVEL SECURITY; -- Only the tip user (application) can read — no PUBLIC access CREATE POLICY foxd_tip_read ON flexoptix_internal_demand FOR SELECT TO tip USING (is_internal = TRUE); CREATE POLICY foxd_tip_write ON flexoptix_internal_demand FOR ALL TO tip USING (TRUE) WITH CHECK (is_internal = TRUE); -- Aggregated demand view: technology-level rollup (safe to expose — no individual SKUs) CREATE OR REPLACE VIEW v_demand_by_speed AS SELECT t.speed_gbps, t.form_factor, COUNT(DISTINCT d.sku) AS sku_count, SUM(d.demand_12m) AS total_demand_12m, SUM(d.demand_3m) AS total_demand_3m, ROUND(AVG(d.demand_12m), 2) AS avg_demand_12m, ROUND(AVG(d.demand_trend_pct), 1) AS avg_trend_pct, COUNT(*) FILTER (WHERE d.velocity_class = 'fast_mover') AS fast_movers, COUNT(*) FILTER (WHERE d.velocity_class = 'regular') AS regular, COUNT(*) FILTER (WHERE d.velocity_class = 'slow_mover') AS slow_movers, COUNT(*) FILTER (WHERE d.velocity_class = 'dead_stock') AS dead_stock FROM flexoptix_internal_demand d JOIN transceivers t ON t.id = d.transceiver_id GROUP BY t.speed_gbps, t.form_factor ORDER BY total_demand_12m DESC; COMMENT ON VIEW v_demand_by_speed IS 'Aggregated demand rollup by technology — safe for API exposure (no individual SKUs).';