-- 025_anaf_datornici.sql -- ANAF — Lista contribuabililor cu obligații fiscale restante (datornici). -- Source: https://www.anaf.ro/restante/ (publicare trimestrială, Ord. 558/2016). -- Plus lista albă (contribuabili FĂRĂ datorii) la /restante/listaalba.xhtml. -- -- Bazele legale: ANAF publică trimestrial sumele restante peste plafoane — -- 500.000 lei (mari contribuabili), 250.000 lei (mijlocii), 100.000 lei -- (mici), 10.000 lei (instituții publice). Sub plafon nu se publică. -- -- KILLER USE CASE: cross-reference cu seap.announcements pentru a găsi -- "firme datornice care au câștigat contracte publice" — interzis prin -- art. 165 Legea 98/2016 dacă sunt obligații fiscale executorii. -- -- IMPORTANT — limitări surse de date (2026-05-09): -- 1. anaf.ro/restante/index.xhtml e o aplicație JSF/PrimeFaces cu CAPTCHA -- de tip kaptcha pe submit. Nu e bulk-scrapeable fără OCR/captcha-solver -- pentru cele ~5K-15K rânduri per trimestru (×4 trim × ~10 ani = ~500K). -- 2. data.gov.ro publică UN SINGUR snapshot Q1-2016 (mari/mijlocii/micijuridice -- CSV) — 140,780 rânduri, util ca baseline istoric. -- 3. listafirme.eu agregă ANAF datornici în spatele unui paywall API. -- -- Strategia ingest: -- - Faza 1 (THIS): schema + importer CSV pentru data.gov.ro Q1-2016 snapshot. -- ~140K rânduri reale, validează schema end-to-end. -- - Faza 2 (TODO): scraper cu captcha-solver extern (anti-captcha.com / -- 2captcha) pentru anaf.ro/restante/ live + arhive trimestriale dacă găsim. -- - Faza 3: integrare cu firms.entities pentru profile badges + recipe-uri. CREATE SCHEMA IF NOT EXISTS anaf; -- ── Tabelă principală: datornici per (CUI × dată publicare) ───────────────── CREATE TABLE IF NOT EXISTS anaf.datornici ( cui text NOT NULL, -- fără prefix RO name text, -- denumirea contribuabilului judet text, -- 2026: nu e disponibil în CSV-urile data.gov.ro Q1-2016, dar e expus în XHTML live publication_date date NOT NULL, -- prima zi a trimestrului (2016-01-01 = T1 2016) period_label text NOT NULL, -- 'T1 2016' / 'T2 2024' etc. debtor_category text, -- 'mari' | 'mijlocii' | 'mici' | 'institutii_publice' | 'persoane_fizice' debt_total numeric(20,2), -- suma RON (principal + accesorii la toate cele 4 bugete) debt_principal numeric(20,2), -- suma RON (principal la toate cele 4 bugete) debt_penalty numeric(20,2), -- suma RON (accesorii la toate cele 4 bugete) debt_contested numeric(20,2), -- suma RON contestată (necontestată = total - contested) -- Detaliu per buget (păstrăm pentru forensică, deși total/principal/penalty -- agregat e suficient pentru majoritatea recipes): budget_state_principal numeric(20,2), budget_state_penalty numeric(20,2), budget_state_contested numeric(20,2), budget_social_principal numeric(20,2), budget_social_penalty numeric(20,2), budget_social_contested numeric(20,2), budget_unemployment_principal numeric(20,2), budget_unemployment_penalty numeric(20,2), budget_unemployment_contested numeric(20,2), budget_health_principal numeric(20,2), budget_health_penalty numeric(20,2), budget_health_contested numeric(20,2), source_url text, -- URL original al CSV / XHTML fetched_at timestamptz DEFAULT now(), PRIMARY KEY (cui, publication_date) ); CREATE INDEX IF NOT EXISTS idx_anaf_datornici_cui ON anaf.datornici(cui); CREATE INDEX IF NOT EXISTS idx_anaf_datornici_pub_date ON anaf.datornici(publication_date DESC); CREATE INDEX IF NOT EXISTS idx_anaf_datornici_total ON anaf.datornici(debt_total DESC NULLS LAST); CREATE INDEX IF NOT EXISTS idx_anaf_datornici_category ON anaf.datornici(debtor_category); -- ── Lista albă: firme FĂRĂ obligații restante (eligibile la SEAP) ─────────── -- Se publică separat la /restante/listaalba.xhtml. Mai puțin acționabilă, dar -- utilă pentru a confirma negativ "firma X NU avea datorii când a câștigat -- contractul Y" (când lipsește din .datornici nu înseamnă neapărat că nu -- avea — poate fi sub plafon). CREATE TABLE IF NOT EXISTS anaf.lista_alba ( cui text NOT NULL, name text, publication_date date NOT NULL, period_label text NOT NULL, source_url text, fetched_at timestamptz DEFAULT now(), PRIMARY KEY (cui, publication_date) ); CREATE INDEX IF NOT EXISTS idx_anaf_lista_alba_cui ON anaf.lista_alba(cui); CREATE INDEX IF NOT EXISTS idx_anaf_lista_alba_pub_date ON anaf.lista_alba(publication_date DESC); -- ── View: cea mai recentă publicare per CUI (latest debt status) ──────────── CREATE OR REPLACE VIEW anaf.datornici_latest AS SELECT DISTINCT ON (cui) cui, name, judet, publication_date, period_label, debtor_category, debt_total, debt_principal, debt_penalty, debt_contested FROM anaf.datornici ORDER BY cui, publication_date DESC; COMMENT ON SCHEMA anaf IS 'ANAF (Agenția Națională de Administrare Fiscală) public registries'; COMMENT ON TABLE anaf.datornici IS 'Lista contribuabililor cu obligații restante, publicată trimestrial (Ord. 558/2016)'; COMMENT ON TABLE anaf.lista_alba IS 'Lista albă: contribuabili FĂRĂ obligații restante la data publicării'; COMMENT ON VIEW anaf.datornici_latest IS 'Cel mai recent snapshot al datoriilor per CUI';