transceiver-db/sql/036-transceiver-equivalences.sql
Rene Fichtmueller f8809d999f feat(scraper+api): warehouse stock data pipeline — FS.com v2, SmartOptics v2, Stock API
Scraper changes:
- fs-com.ts v2: Playwright stealth patches + www.fs.com/de/ URL fix (de.fs.com DNS NXDOMAIN).
  Extracts DE-Lager, Global-Lager, Nachlieferung, units_sold, compatible_brands, price_net.
  Mac-side runner (run-fs-scraper-mac.sh) via SSH tunnel for residential IP access.
  Fast-fail connectivity check on datacenter IPs that are blocked by Cloudflare.
- smartoptics.ts v2: WooCommerce REST API fallback + 8 catalog categories + relative URL fix.
  Was finding only 8 products, now discovers 18+ with multi-category crawl.

DB layer:
- db.ts: add upsertStockObservation() — writes 10 new stock_observations columns
  (warehouse_de_qty, warehouse_global_qty, backorder_qty, units_sold, compatible_brands,
  price_net, product_url, delivery dates) with dedup check.

API:
- routes/stock.ts: GET /api/stock, /api/stock/summary, /api/stock/:id
  Warehouse breakdowns per transceiver/vendor with top-sellers and vendor summary.
- routes/review.ts: equivalence review queue (approve/reject/bulk-approve).
- index.ts: register /api/stock and /api/review routes.

Dashboard:
- index.html: 🏭 Stock tab with stat cards (DE-Lager, Global-Lager, Nachlieferung totals),
  top-sellers table, vendor breakdown, recently-restocked events, part-number lookup.

SQL migrations:
- 034: blog-review-tag, 035: price-observations is_anomalous, 036: transceiver-equivalences.
2026-04-17 10:45:59 +02:00

45 lines
2.1 KiB
PL/PgSQL

-- Migration 036: Transceiver equivalences for competitor_verified matching
-- Stores semantic equivalences between Flexoptix SKUs and competitor products
-- matched by technical specs (form_factor + speed + reach + standard + fiber_type)
CREATE TABLE IF NOT EXISTS transceiver_equivalences (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
flexoptix_id UUID NOT NULL REFERENCES transceivers(id) ON DELETE CASCADE,
competitor_id UUID NOT NULL REFERENCES transceivers(id) ON DELETE CASCADE,
confidence DECIMAL(4,3) NOT NULL CHECK (confidence BETWEEN 0 AND 1),
match_basis TEXT[] NOT NULL DEFAULT '{}', -- ['standard_name','form_factor','speed_gbps','fiber_type','reach']
match_notes TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','approved','rejected','auto_approved')),
reviewed_by VARCHAR(200),
reviewed_at TIMESTAMPTZ,
reject_reason TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (flexoptix_id, competitor_id)
);
CREATE INDEX IF NOT EXISTS idx_eq_flexoptix ON transceiver_equivalences (flexoptix_id);
CREATE INDEX IF NOT EXISTS idx_eq_competitor ON transceiver_equivalences (competitor_id);
CREATE INDEX IF NOT EXISTS idx_eq_status ON transceiver_equivalences (status, confidence DESC);
CREATE INDEX IF NOT EXISTS idx_eq_pending ON transceiver_equivalences (flexoptix_id) WHERE status = 'pending';
-- Auto-update updated_at
CREATE OR REPLACE FUNCTION update_equivalences_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_eq_updated_at ON transceiver_equivalences;
CREATE TRIGGER trg_eq_updated_at
BEFORE UPDATE ON transceiver_equivalences
FOR EACH ROW EXECUTE FUNCTION update_equivalences_updated_at();
COMMENT ON TABLE transceiver_equivalences IS
'Semantic equivalences between Flexoptix SKUs and competitor products, '
'matched by technical specification overlap. Used to set competitor_verified=true '
'on Flexoptix transceivers that have no exact SKU match at competitors.';