feat(procurement): data-grounded supply-availability signal per speed tier (1.6T scarce/800G moderate/400G abundant from real supplier+stock data)

This commit is contained in:
Rene Fichtmueller 2026-06-11 09:19:57 +00:00
parent de40c65b01
commit 5d69f0160c

View File

@ -781,6 +781,100 @@ procurementRouter.get("/dead-stock-revival", async (_req: Request, res: Response
}); });
// ─── C: GET /api/procurement/supply-squeeze ────────────────────────────────── // ─── C: GET /api/procurement/supply-squeeze ──────────────────────────────────
// ─── GET /api/procurement/availability ───────────────────────────────────────
// Data-grounded supply-availability per speed tier. Derived from real supplier
// diversity (distinct source vendors offering it) + stock coverage. Surfaces the
// 400G/800G/1.6T supply tightening that exists in the data but was not shown.
procurementRouter.get("/availability", async (_req: Request, res: Response) => {
const client = await pool.connect();
try {
// Disable parallel workers for this aggregate — Docker /dev/shm (64MB) is too
// small for parallel hash and the query is fast enough single-threaded.
await client.query('BEGIN');
await client.query('SET LOCAL max_parallel_workers_per_gather = 0');
const result = await client.query(`
WITH sup AS (
SELECT t.speed_gbps,
COUNT(DISTINCT po.source_vendor_id) FILTER (WHERE po.time > NOW() - INTERVAL '45 days') AS suppliers,
COUNT(DISTINCT po.source_vendor_id) FILTER (WHERE po.time <= NOW() - INTERVAL '45 days') AS suppliers_prior
FROM price_observations po
JOIN transceivers t ON t.id = po.transceiver_id
WHERE t.speed_gbps IN (100,200,400,800,1600) AND po.time > NOW() - INTERVAL '90 days'
GROUP BY t.speed_gbps
),
sk AS (
SELECT speed_gbps, COUNT(*) AS sku_count
FROM transceivers WHERE speed_gbps IN (100,200,400,800,1600)
GROUP BY speed_gbps HAVING COUNT(*) >= 3
),
st AS (
SELECT t.speed_gbps,
COUNT(*) FILTER (WHERE s.in_stock IS TRUE) AS skus_in_stock,
COUNT(*) AS skus_with_stock
FROM transceivers t
JOIN LATERAL (
SELECT in_stock FROM stock_observations so
WHERE so.transceiver_id = t.id ORDER BY so.time DESC LIMIT 1
) s ON true
WHERE t.speed_gbps IN (100,200,400,800,1600)
GROUP BY t.speed_gbps
)
SELECT
sk.speed_gbps,
sk.sku_count::int,
COALESCE(sup.suppliers,0)::int AS suppliers,
COALESCE(sup.suppliers_prior,0)::int AS suppliers_prior,
COALESCE(st.skus_in_stock,0)::int AS skus_in_stock,
COALESCE(st.skus_with_stock,0)::int AS skus_with_stock,
CASE WHEN COALESCE(st.skus_with_stock,0) > 0
THEN ROUND(100.0 * st.skus_in_stock / st.skus_with_stock)::int ELSE NULL END AS in_stock_pct
FROM sk
LEFT JOIN sup ON sup.speed_gbps = sk.speed_gbps
LEFT JOIN st ON st.speed_gbps = sk.speed_gbps
ORDER BY sk.speed_gbps DESC
`);
type Row = { speed_gbps: string; sku_count: number; suppliers: number; suppliers_prior: number; skus_in_stock: number; skus_with_stock: number; in_stock_pct: number | null };
const tiers = (result.rows as Row[]).map((r) => {
const sup = r.suppliers ?? 0;
const inStockPct = r.in_stock_pct ?? null;
// availability class from supplier diversity + stock coverage
let availability: "scarce" | "constrained" | "moderate" | "abundant";
if (sup <= 3 || (inStockPct !== null && inStockPct < 20)) availability = "scarce";
else if (sup <= 6 || (inStockPct !== null && inStockPct < 45)) availability = "constrained";
else if (sup <= 9) availability = "moderate";
else availability = "abundant";
// tightening trend: supplier diversity dropped vs prior window
const trend = r.suppliers_prior > 0
? (sup < r.suppliers_prior ? "tightening" : sup > r.suppliers_prior ? "loosening" : "stable")
: "stable";
const drivers: string[] = [`${sup} active suppliers`];
if (inStockPct !== null) drivers.push(`${inStockPct}% of tracked SKUs in stock`);
drivers.push(`${r.sku_count} SKUs`);
if (trend === "tightening") drivers.push(`supplier base shrinking (${r.suppliers_prior}${sup})`);
return {
speed_gbps: parseFloat(r.speed_gbps),
sku_count: r.sku_count,
suppliers: sup,
in_stock_pct: inStockPct,
availability,
trend,
drivers,
};
});
res.json({ success: true, tiers });
} catch (err) {
try { await client.query('ROLLBACK'); } catch { /* ignore */ }
console.error("availability error:", err);
res.status(500).json({ success: false, error: String(err) });
} finally {
client.release();
}
});
// Multi-signal supply constraint detector // Multi-signal supply constraint detector
procurementRouter.get("/supply-squeeze", async (_req: Request, res: Response) => { procurementRouter.get("/supply-squeeze", async (_req: Request, res: Response) => {
try { try {