Files
vreau-digital/services/seap-scraper/sql/034_asf.sql
T
Claude VM a6c03a091e initial: split from gov-agreg — vreau.digital standalone platform
Moved from gov-agreg/src/pages/achizitii/* to root (drop prefix).
- 22 pages migrated, 127 files total
- All internal links: /achizitii/X → /X (176 occurrences fixed)
- AchizitiiLayout subnav rewritten: /X paths, top-right link to vreaudigital.ro hub
- BaseLayout new (vreau.digital branding, OG tags, site URL)
- astro.config.mjs: site https://vreau.digital, server output (was static)
- docker-compose: port 5096 (vreaudigital is 5095), container vreau-digital
- deploy.sh: paths /opt/vreau-digital, log /var/log/vreau-digital-deploy.log

Backend shared with gov-agreg:
- PostgreSQL satra (same schemas: seap, firms, anaf, anre, ...)
- Photon, Martin tiles
- Infisical /vreaudigital path (DATABASE_URL etc. shared)

build: PASS (npx astro check 0 errors, npm run build 5s vite + 10s server)
2026-05-13 00:10:32 +03:00

124 lines
7.3 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 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';