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

97 lines
5.6 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.
-- 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';