-- 034_asf.sql -- ASF — Autoritatea de Supraveghere Financiară. -- Public registries of authorized financial entities (insurers, brokers, pension -- funds, asset managers, intermediaries) scraped from data.asfromania.ro. -- -- Sources (all return JSON{raspuns:HTML, status:100} via POST cautare): -- 1. /scr/ra/cautare?l=ro (Registrul asigurătorilor + intermediarilor) -- sectiune=1 tipCompanie=0 → Societăți de asigurare - companii active -- sectiune=2 tipCompanie=0 → Societăți de asigurare - companii radiate -- sectiune=1 tipCompanie=1 → Intermediari principali - companii active -- sectiune=2 tipCompanie=1 → Intermediari principali - companii radiate -- Fields per panel: register_no (RA-XXX/RBK-XXX), LEI, CUI, RC code, -- authorization no/date, registration date, radiation date, type, legal form, -- address, phone, fax, observations, authorized classes (general/life), -- executives. Total: ~768 insurers + ~801 brokers ≈ 1.5K entities. -- -- 2. /scr/ra/cautare endpoint accepts free-text 'termen' (≥4 chars). Search -- hits denumire, CUI, adresă, județ, classes. NO captcha required when -- 'g-recaptcha-response' field is OMITTED from the POST body. (When sent -- with any non-empty value the server tries to verify and returns -- "Verificare captcha eșuată".) -- -- 3. Pension funds + AIFM/UCITS register pages exist on asfromania.ro/ro/a/... -- but most are F5-WAF-protected from non-browser clients. We start with the -- ra portal which has cleanest data; document handoff for additional -- registers in ASF-PLAN.md. -- -- Cross-source value: asf.entitati.cui (extracted directly from response, no -- fuzzy match needed) × seap.announcements.supplier_cui = "ASF-licensed firms -- with state contracts". Red-flag: insurance firm wins SEAP contract for state -- insurance services but has been radiated by ASF; broker active in SEAP but -- with suspended/withdrawn ASF authorization. CREATE SCHEMA IF NOT EXISTS asf; -- ── 1. Authorized entities (insurers, brokers, pension funds, AIFM, UCITS) ── -- One row per distinct ASF register entry. Every entity has a register_no -- (RA-NNN for insurers, RBK-NNN for brokers, etc.) which is globally unique -- per register_type. CREATE TABLE IF NOT EXISTS asf.entitati ( id bigserial PRIMARY KEY, register_type text NOT NULL, -- 'asigurator' | 'broker' | 'fond_pensii' | 'aifm' | 'ucits' | 'intermediar_secundar' section_status text NOT NULL, -- 'activ' | 'radiat' (mirrors source sectiune=1/2 split) register_no text NOT NULL, -- e.g. "RA-057", "RBK-123" (unique within register_type) name text NOT NULL, -- raw "Denumire" name_normalized text, -- firms.normalize_company_name(name) — for trigram fallback cui text, -- "Cod unic de identificare (CUI)" cod_rc text, -- "Cod unic RC" (e.g. J40/2226/2006) cod_lei text, -- LEI 20-char nr_autorizatie text, -- "Număr autorizație" (e.g. 114.146) data_autorizare date, -- "Dată autorizare" data_inmatriculare date, -- "Dată înmatriculare" data_radiere date, -- "Dată radiere" (NULL when active) tip_companie text, -- "Tip companie" (Societate de asigurare / Intermediar principal / etc.) forma_juridica text, -- "Formă juridică" adresa text, -- "Adresă" telefon text, fax text, email text, web text, observatii text, -- free-text remarks clase_autorizate jsonb, -- {"asigurari_generale":[...], "asigurari_viata":[...]} conducere jsonb, -- [{"nume":"X","functie":"Y","din":"DD.MM.YYYY"}] raw_html text, -- raw panel HTML for traceability fetched_at timestamptz NOT NULL DEFAULT now(), UNIQUE (register_type, register_no) ); CREATE INDEX IF NOT EXISTS idx_asf_entitati_cui ON asf.entitati(cui) WHERE cui IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_asf_entitati_name_norm_trgm ON asf.entitati USING gin (name_normalized gin_trgm_ops); CREATE INDEX IF NOT EXISTS idx_asf_entitati_type_status ON asf.entitati(register_type, section_status); CREATE INDEX IF NOT EXISTS idx_asf_entitati_radiere ON asf.entitati(data_radiere) WHERE data_radiere IS NOT NULL; COMMENT ON TABLE asf.entitati IS 'ASF authorized entities — insurers, brokers, pension funds, AIFM/UCITS, intermediaries. Source: data.asfromania.ro/scr/ra (and other registers).'; COMMENT ON COLUMN asf.entitati.register_type IS 'asigurator (RA-NNN) / broker (RBK-NNN) / fond_pensii / aifm / ucits / intermediar_secundar'; COMMENT ON COLUMN asf.entitati.section_status IS 'activ / radiat — mirrors source sectiune=1/sectiune=2 split. Active record has data_radiere=NULL.'; -- ── 2. Scrape log (mirrors anre.scrape_log convention) ────────────────────── CREATE TABLE IF NOT EXISTS asf.scrape_log ( id bigserial PRIMARY KEY, scraper text NOT NULL, -- 'asigurator_activ' / 'asigurator_radiat' / 'broker_activ' / ... source_url text NOT NULL, rows_seen integer NOT NULL DEFAULT 0, rows_inserted integer NOT NULL DEFAULT 0, rows_updated integer NOT NULL DEFAULT 0, rows_skipped integer NOT NULL DEFAULT 0, duration_ms integer NOT NULL DEFAULT 0, started_at timestamptz NOT NULL, finished_at timestamptz NOT NULL DEFAULT now(), error text ); CREATE INDEX IF NOT EXISTS idx_asf_scrape_log_started ON asf.scrape_log(started_at DESC); -- ── 3. Materialized view: per-CUI ASF rollup ──────────────────────────────── -- Joinable with seap.announcements.supplier_cui to detect financial firms -- holding state contracts. CREATE MATERIALIZED VIEW IF NOT EXISTS asf.mv_entitati_per_cui AS SELECT cui, COUNT(*) AS nr_total, COUNT(*) FILTER (WHERE register_type = 'asigurator') AS nr_asigurator, COUNT(*) FILTER (WHERE register_type = 'broker') AS nr_broker, COUNT(*) FILTER (WHERE register_type = 'fond_pensii') AS nr_fond_pensii, COUNT(*) FILTER (WHERE register_type = 'aifm') AS nr_aifm, COUNT(*) FILTER (WHERE register_type = 'ucits') AS nr_ucits, COUNT(*) FILTER (WHERE section_status = 'activ') AS nr_active, COUNT(*) FILTER (WHERE section_status = 'radiat') AS nr_radiate, array_agg(DISTINCT register_type) AS register_types, array_agg(DISTINCT register_no ORDER BY register_no) AS register_numbers, MIN(data_autorizare) AS prima_autorizare, MAX(data_radiere) AS ultima_radiere FROM asf.entitati WHERE cui IS NOT NULL GROUP BY cui; CREATE UNIQUE INDEX IF NOT EXISTS idx_asf_mv_entitati_per_cui ON asf.mv_entitati_per_cui(cui); COMMENT ON MATERIALIZED VIEW asf.mv_entitati_per_cui IS 'Rollup of ASF entities per CUI. Refresh: REFRESH MATERIALIZED VIEW CONCURRENTLY asf.mv_entitati_per_cui';