Files
vreau-digital/services/seap-scraper/sql/024_aep_donatii.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

204 lines
13 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.
-- 024_aep_donatii.sql
-- AEP — Autoritatea Electorală Permanentă — donații electorale & finanțare partide.
--
-- Sursă oficială (mandatată prin Legea 334/2006): rapoartele anuale + rapoartele
-- de venituri și cheltuieli (RVC) ale partidelor + listele de donatori publicate
-- în Monitorul Oficial pentru donații > 10 salarii minime brute.
--
-- Vehicul de ingest: portalul Expert Forum (banipartide.ro) care a aggregat-o
-- deja în SQLite și o expune via endpoint base64-SQL la
-- https://www.banipartide.ro/app/json.php?mode=dt&ssid=<base64>.
-- (Sursele primare AEP sunt PDF/Excel + reCAPTCHA, deci EFOR este path de
-- minim efort. Validate against AEP RVC PDFs as v2.)
--
-- Volume @ 2026-05-09:
-- Donatori persoane juridice (>10 sal MO): 3,612 (2006-2024)
-- Donatori persoane fizice (>10 sal MO): 30,792 (2006-2024)
-- Donatori RVC (rapoarte venituri/cheltuieli, granular complet): 353,473
--
-- GDPR: CNP-urile sunt expuse în clear pe banipartide.ro (publicate în MO conf.
-- legii). Le hash-ăm SHA256 pe ingest — la noi NU stocăm CNP raw. Numele
-- complet e public prin lege și rămâne. Adresa pe firme (PJ) e public,
-- la persoane fizice (PF) NU avem adresă la sursă.
--
-- Cross-source value: aep.donatii_pj.donator_cui seap.announcements.supplier_cui
-- = "donator X a donat Y RON partidului Z, apoi a câștigat W RON contracte SEAP".
CREATE SCHEMA IF NOT EXISTS aep;
COMMENT ON SCHEMA aep IS
'Autoritatea Electorală Permanentă — donații, finanțare partide, RVC. Sursă: banipartide.ro (EFOR) → AEP/MO.';
-- ──────────────────────────────────────────────────────────────────────────
-- aep.partide — registru partide normalizat (codes from banipartide source)
-- ──────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS aep.partide (
id text PRIMARY KEY, -- 'PSD', 'PNL', 'USR', 'AUR', 'UDMR', etc.
nume_oficial text, -- 'Partidul Social Democrat'
fondat date,
sediu_cui text, -- CIF al partidului dacă cunoscut
status text, -- 'activ' | 'dizolvat' | 'fuzionat'
fetched_at timestamptz DEFAULT now()
);
COMMENT ON TABLE aep.partide IS
'Registru partide politice (cheie naturală = abreviere normalizată din sursa banipartide).';
-- ──────────────────────────────────────────────────────────────────────────
-- aep.donatii_pj — donații de la persoane juridice (>10 salarii minime, MO)
-- ──────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS aep.donatii_pj (
id bigserial PRIMARY KEY,
source_hash char(40) NOT NULL UNIQUE, -- sha1(nume|cui|partid|an|suma|data_donatie) for idempotent upsert
donator_nume text NOT NULL,
donator_cui text, -- CUI normalizat (numerals only, RO prefix stripped)
donator_cui_raw text, -- forma originală (poate avea typos / "RO")
reprezentant text,
sediu text,
nationalitate text, -- "română" / etc.
partid_id text REFERENCES aep.partide(id) ON UPDATE CASCADE,
filiala_partid text,
suma_lei numeric(14,2) NOT NULL,
an smallint NOT NULL,
data_donatie_text text, -- format mixt în sursă: "11.10.2019; 13.11.2019" sau "10042010" — păstrăm raw
data_donatie date, -- best-effort parsed (NULL când format incompatibil sau multiple)
tip_donatie text, -- "Bani" / "Natură" / etc.
felul_donatie text, -- "Bani" / "Ordin De Plată" / "Spațiu Publicitar"
source_url text NOT NULL DEFAULT 'https://www.banipartide.ro/donatori-persoane-juridice.html',
fetched_at timestamptz DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pj_cui ON aep.donatii_pj(donator_cui) WHERE donator_cui IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pj_partid ON aep.donatii_pj(partid_id);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pj_an ON aep.donatii_pj(an);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pj_suma ON aep.donatii_pj(suma_lei DESC);
COMMENT ON TABLE aep.donatii_pj IS
'Donații de la persoane juridice către partide, peste pragul de 10 salarii minime brute (publicate în MO). Sursă: banipartide.ro → AEP. Granularitate: o linie per (donator, partid, an, sumă, dată).';
COMMENT ON COLUMN aep.donatii_pj.source_hash IS
'sha1(nume_lower|cui|partid|an|suma|data_text). Garantează idempotenta scraperului.';
-- ──────────────────────────────────────────────────────────────────────────
-- aep.donatii_pf — donații de la persoane fizice (>10 sal min, MO)
-- CNP hash-uit (NICIODATĂ raw în DB).
-- ──────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS aep.donatii_pf (
id bigserial PRIMARY KEY,
source_hash char(40) NOT NULL UNIQUE, -- sha1(nume|cnp_hash|partid|an|suma|data)
donator_nume text NOT NULL,
donator_cnp_sha256 char(64), -- SHA-256 hex of CNP (only if CNP was non-empty in source)
partid_id text REFERENCES aep.partide(id) ON UPDATE CASCADE,
organizatia text, -- filiala / organizatia partidului
suma_lei numeric(14,2) NOT NULL,
an smallint NOT NULL,
data_donatie_text text,
data_donatie date,
tip_donatie text, -- "Donație" / "Cotizație" / "Împrumut"
ce_s_a_donat text, -- "Bani" / "Bunuri" / etc.
source_url text NOT NULL DEFAULT 'https://www.banipartide.ro/donatori-persoane-fizice.html',
fetched_at timestamptz DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pf_cnp_hash ON aep.donatii_pf(donator_cnp_sha256) WHERE donator_cnp_sha256 IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pf_nume ON aep.donatii_pf(donator_nume);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pf_partid ON aep.donatii_pf(partid_id);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_pf_an ON aep.donatii_pf(an);
COMMENT ON TABLE aep.donatii_pf IS
'Donații de la persoane fizice către partide, peste pragul de 10 salarii minime (publicate în MO). CNP-urile sunt SHA-256 hashed la ingest. Sursă: banipartide.ro.';
COMMENT ON COLUMN aep.donatii_pf.donator_cnp_sha256 IS
'SHA-256 hex digest al CNP. Permite re-identificare dacă cineva are CNP-ul, dar nu dezvăluie CNP-ul. NU e key-uit cu salt — scopul e doar de-duplicare cross-an, nu protecție criptografică împotriva brute-force pe spațiul CNP-urilor românești.';
-- ──────────────────────────────────────────────────────────────────────────
-- aep.donatii_rvc — toți donatorii din rapoartele de venituri/cheltuieli
-- (donații + cotizații + împrumuturi, fără pragul de 10 salarii)
-- ──────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS aep.donatii_rvc (
id bigserial PRIMARY KEY,
source_hash char(40) NOT NULL UNIQUE,
donator_nume text NOT NULL,
judet text, -- "Alba", "București"
cod_judet text, -- "AB", "B"
tip_venit text, -- "Cotizație" | "Donație" | "Împrumut"
partid_id text REFERENCES aep.partide(id) ON UPDATE CASCADE,
suma_lei numeric(14,2) NOT NULL,
mod_incasare text, -- "Banca" | "Numerar" | etc.
an smallint NOT NULL,
data_donatie_text text,
data_donatie date,
source_url text NOT NULL DEFAULT 'https://www.banipartide.ro/donatori-rvc.html',
fetched_at timestamptz DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_rvc_partid ON aep.donatii_rvc(partid_id);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_rvc_an ON aep.donatii_rvc(an);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_rvc_nume ON aep.donatii_rvc(donator_nume);
CREATE INDEX IF NOT EXISTS idx_aep_donatii_rvc_judet ON aep.donatii_rvc(judet);
COMMENT ON TABLE aep.donatii_rvc IS
'Toate donațiile/cotizațiile/împrumuturile din rapoartele de venituri și cheltuieli (RVC) ale partidelor, fără pragul de 10 salarii. ~353K rânduri. Sursă: banipartide.ro → AEP.';
-- ──────────────────────────────────────────────────────────────────────────
-- aep.scrape_log — audit trail al scraperelor (per tabel × per zi)
-- ──────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS aep.scrape_log (
id bigserial PRIMARY KEY,
scraper text NOT NULL, -- 'donatii_pj' | 'donatii_pf' | 'donatii_rvc'
source_url text NOT NULL,
rows_seen integer NOT NULL,
rows_inserted integer NOT NULL,
rows_updated integer NOT NULL,
rows_skipped integer NOT NULL,
duration_ms integer NOT NULL,
started_at timestamptz NOT NULL,
finished_at timestamptz NOT NULL DEFAULT now(),
error text
);
CREATE INDEX IF NOT EXISTS idx_aep_scrape_log_scraper ON aep.scrape_log(scraper, started_at DESC);
-- ──────────────────────────────────────────────────────────────────────────
-- Materialized view: agregare per CUI pentru profile firmă rapid.
-- (refresh-ed by cron post-scrape; see refresh-mvs.sh)
-- ──────────────────────────────────────────────────────────────────────────
CREATE MATERIALIZED VIEW IF NOT EXISTS aep.mv_donatii_per_cui AS
SELECT
donator_cui AS cui,
COUNT(*) AS nr_donatii,
SUM(suma_lei) AS total_lei,
COUNT(DISTINCT partid_id) AS nr_partide,
array_agg(DISTINCT partid_id)
FILTER (WHERE partid_id IS NOT NULL) AS partide,
MIN(an) AS prima_donatie_an,
MAX(an) AS ultima_donatie_an
FROM aep.donatii_pj
WHERE donator_cui IS NOT NULL
GROUP BY donator_cui;
CREATE UNIQUE INDEX IF NOT EXISTS idx_aep_mv_donatii_per_cui ON aep.mv_donatii_per_cui(cui);
COMMENT ON MATERIALIZED VIEW aep.mv_donatii_per_cui IS
'Pre-aggregat pentru profile firmă: donații totale per CUI. Refresh după fiecare scrape.';
-- ──────────────────────────────────────────────────────────────────────────
-- Materialized view: top donatori per partid (folosit pe pagini publice)
-- ──────────────────────────────────────────────────────────────────────────
CREATE MATERIALIZED VIEW IF NOT EXISTS aep.mv_top_donatori_partid AS
SELECT
partid_id,
donator_nume,
donator_cui,
COUNT(*) AS nr_donatii,
SUM(suma_lei) AS total_lei,
MIN(an) AS prima_donatie_an,
MAX(an) AS ultima_donatie_an
FROM aep.donatii_pj
WHERE partid_id IS NOT NULL
GROUP BY partid_id, donator_nume, donator_cui;
CREATE INDEX IF NOT EXISTS idx_aep_mv_top_donatori_partid_partid ON aep.mv_top_donatori_partid(partid_id, total_lei DESC);
CREATE INDEX IF NOT EXISTS idx_aep_mv_top_donatori_partid_cui ON aep.mv_top_donatori_partid(donator_cui) WHERE donator_cui IS NOT NULL;
COMMENT ON MATERIALIZED VIEW aep.mv_top_donatori_partid IS
'Top donatori per partid pentru afișare publică. Datele sunt deja publice prin lege (MO).';