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)
This commit is contained in:
Claude VM
2026-05-13 00:10:32 +03:00
commit a6c03a091e
352 changed files with 75295 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
-- 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';