Skip to main content

anaf-sync

CI Coverage PyPI version Python versions

Arhivator local, programat, pentru facturile RO e-Factura, construit peste anafpy. ANAF șterge mesajele din SPV la circa 60 de zile după depunere; anaf-sync rulează periodic, listează toată fereastra de retenție, descarcă doar ce nu a mai văzut și așază facturile pe disc după un șablon de căi construit din datele facturii (2026/07/2026-07-03_FCT-1001_ACME SRL.pdf, nu id-uri opace ANAF). Rulează pe Windows, Linux și macOS.

English: anaf-sync archives RO e-Factura invoices locally on a schedule. User docs are in Romanian because the tool only serves entities with Romanian fiscal obligations. Developer docs are in English — see CONTRIBUTING.md and DESIGN.md.

Instalare

uv tool install anaf-sync        # dintr-un wheel publicat
# sau, din acest checkout:
uv tool install --from . anaf-sync

Ai nevoie de uv, care își instalează singur Python-ul potrivit.

Autentificare

anaf-sync nu are un sistem propriu de credențiale: refolosește autentificarea anafpy — același login servește și serverul MCP anafpy. Certificatul digital e necesar doar la autorizarea inițială din browser, cam o dată pe an; după aceea token-urile se reîmprospătează automat, fără certificat, deci rulările programate merg nesupravegheate.

Pasul 1 — precondiții pe portalul ANAF (o singură dată)

  1. Certificat digital calificat (token USB de la certSIGN, DigiSign, Trans Sped, AlfaSign etc.), înregistrat în SPV pentru firma ta. Dacă accesezi deja Spațiul Privat Virtual al firmei cu certificatul, ești gata.
  2. Înregistrare ca dezvoltator de aplicații, pe anaf.ro: Servicii Online → Înregistrare utilizatori → Dezvoltatori aplicații → Înregistrare pentru API-uri. Confirmarea vine printr-un cod de securitate trimis pe e-mail.

Pasul 2 — profilul OAuth (client_id + client_secret)

Tot pe portal, în formularul Profil Oauth, completezi:

Câmp Ce pui
Denumire aplicație orice nume, de ex. anaf-sync
Callback URL 1 de ex. https://localhost:8765/callback — schema trebuie să fie https:// (portalul respinge http://); poate fi localhost, nu îți trebuie un server public
Serviciu E-Factura

Apeși Generare Client ID și primești un Client ID și un Client Secret — „parola" aplicației; păstrează-le în siguranță.

Pasul 3 — login

export ANAFPY_CLIENT_ID=...          # sau într-un fișier .env
export ANAFPY_CLIENT_SECRET=...

anafpy auth login --redirect-uri https://localhost:8765/callback --paste

Se deschide browserul, îți alegi certificatul, iar ANAF redirecționează către callback. Cu --paste nu pornește niciun server local: browserul va afișa o eroare de conexiune, dar bara de adrese conține URL-ul complet cu codul de autorizare — îl copiezi în terminal. (Alternativ, cu un certificat TLS local — de ex. generat cu mkcert--tls-cert/--tls-key capturează redirectul automat, fără copiat.)

Token-urile se salvează în credential store-ul sistemului de operare. Pe mașini fără credential store (servere headless), folosește varianta pe fișier: ANAFPY_TOKEN_STORE_BACKEND=file și ANAFPY_TOKEN_STORE=~/.anafpy/tokens.json.

ANAFPY_CLIENT_ID și ANAFPY_CLIENT_SECRET trebuie să rămână setate (în mediu sau în .env) și după login: cu ele își reîmprospătează rulările programate token-urile expirate. Token-ul de acces ține ~90 de zile, refresh-ul ~365 — browserul și certificatul revin în joc doar când expiră și acesta.

Atenție la .env + rulări programate: un .env din directorul curent funcționează doar interactiv — joburile programate (Task Scheduler, systemd, launchd) nu pornesc din folderul tău și nu citesc profilul shell-ului. Pentru rulările programate pune .env-ul cu variabilele ANAFPY_* lângă config.toml, în directorul de configurare (calea o vezi cu anaf-sync status; tot acolo verifici și dacă credențialele sunt găsite).

Configurare

anaf-sync init            # scrie un config.toml comentat
anaf-sync status          # arată unde se află fișierul pe platforma ta

Partea interesantă e șablonul de căi:

[output]
directory = "~/Facturi"
template  = "{cif}/{direction}/{issue_date:%Y}/{issue_date:%m}/{issue_date:%Y-%m-%d}_{number}_{partner_name}"
artifacts = ["zip", "pdf"]        # și: xml, signature, metadata

Șabloanele folosesc sintaxa str.format din Python peste contextul facturii: number, issue_date / due_date (date reale — specificatorii strftime funcționează), issue_month / created_month (numele lunii în română: iulie), currency, kind, direction, cif, partner_name/partner_cif (cealaltă parte, indiferent de direcție), message_id, request_id, message_type, created. Valorile substituite sunt sanitizate pentru sistemul de fișiere; un / literal în șablon creează foldere; fiecare artefact își adaugă propria extensie.

Primele variabile din listă se completează din XML-ul facturii, deci pentru mesajele fără XML (fișiere de eroare, mesaje de la cumpărător) devin unknown. Doar cif, direction, message_id, request_id, message_type, created și created_month există întotdeauna — un șablon construit exclusiv din celelalte adună toate aceste mesaje pe aceeași cale.

Orice variabilă acceptă o conversie de capitalizare: {issue_month!u}IULIE, {issue_month!c}Iulie, {issue_month!l}iulie (implicit numele lunilor sunt cu literă mică, conform normelor limbii române), iar {partner_name!t}Furnizor Srl (fiecare cuvânt cu majusculă). Pentru foldere sortate cronologic, combinați numărul și numele lunii: {issue_date:%m}-{issue_month}07-iulie.

Rulare

anaf-sync sync --dry-run   # arată ce s-ar descărca, fără să scrie nimic
anaf-sync sync             # descarcă tot ce e nou

Rulările sunt idempotente: un fișier de stare reține id-urile mesajelor deja arhivate, așa că ferestrele de 60 de zile care se suprapun nu duplică niciodată nimic, iar ce urmează ANAF să șteargă a fost deja capturat.

Programare

anaf-sync schedule install --every 6h        # sau --daily-at 07:30
anaf-sync schedule status
anaf-sync schedule remove

Aceasta înregistrează sincronizarea în planificatorul nativ al sistemului — Task Scheduler pe Windows, un timer systemd de utilizator pe Linux (loginctl enable-linger $USER ca să ruleze și fără sesiune deschisă), launchd pe macOS. Fără daemon propriu.

Aplicația din bara de sistem (opțional)

Un companion desktop discret afișează starea arhivei printr-o iconiță în bara de sistem, ca să vezi din timp când o sincronizare se strică — înainte ca ANAF să șteargă mesajele după 60 de zile. Culoarea punctului de stare înseamnă:

  • verde — arhiva este la zi;
  • galben — necesită atenție: o factură eșuează repetat sau a fost declarată cu întârziere;
  • roșu — sincronizarea nu funcționează (de obicei autentificarea ANAF a expirat — rulează anafpy auth login).

Din meniu poți porni o sincronizare, deschide dosarul arhivei, răsfoi facturile arhivate și edita configurația — fără să atingi config.toml manual (deși rămâne editabil manual oricând). Aplicația doar citește arhiva și scrie config.toml; orice descărcare o face tot anaf-sync sync.

Instalare (adaugă dependențele grafice PySide6):

pip install "anaf-sync[tray]"
anaf-sync-tray                 # pornește aplicația
anaf-sync tray install         # pornire automată la logare (idempotent)
anaf-sync tray status
anaf-sync tray remove

Alternativ, descarcă un pachet gata compilat de la secțiunea Releases (nu necesită Python). Pachetele nu sunt semnate deocamdată, așa că la prima pornire sistemul afișează un avertisment: pe macOS deschide-l cu click‑dreapta → „Open" o singură dată; pe Windows alege „More info" → „Run anyway".

Pe Linux/GNOME iconițele din bară au nevoie de extensia AppIndicator („AppIndicator and KStatusNotifierItem Support"); pe majoritatea celorlalte medii desktop funcționează direct.

Jurnale

Rulările interactive afișează jurnale lizibile în consolă. Rulările programate (orice rulare fără TTY) scriu direct în facilitatea nativă de jurnalizare a platformei, deci le inspectezi cu uneltele sistemului — fără fișiere de log proprii:

# Windows — jurnalul de evenimente Application, sursa "anaf-sync"
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='anaf-sync'} -MaxEvents 20
# macOS — unified log, subsistemul "ro.anaf-sync"
log show --last 1d --info --predicate 'subsystem == "ro.anaf-sync"'
log stream --predicate 'subsystem == "ro.anaf-sync"'   # live, în timpul unui sync

# Linux — journald (și: journalctl --user -u anaf-sync.service)
journalctl --user SYSLOG_IDENTIFIER=anaf-sync --since today
journalctl --user SYSLOG_IDENTIFIER=anaf-sync -p err   # doar erorile

Fiecare rulare emite un eveniment-sumar sync_done plus evenimente per-mesaj (archived, download_failed, …); severitățile se mapează pe nivelurile native, deci filtrele „doar erori" funcționează peste tot. Setează ANAF_SYNC_LOG=console sau =system ca să forțezi modul, peste detecția de TTY.

Dezvoltare

Documentația pentru dezvoltatori e în engleză: CONTRIBUTING.md (setup și quality gates), DESIGN.md (rațiunea arhitecturii).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anaf_sync-0.2.0.tar.gz (241.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anaf_sync-0.2.0-py3-none-any.whl (100.7 kB view details)

Uploaded Python 3

File details

Details for the file anaf_sync-0.2.0.tar.gz.

File metadata

  • Download URL: anaf_sync-0.2.0.tar.gz
  • Upload date:
  • Size: 241.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for anaf_sync-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f3a3ebe3e031da6dc12142c3134c7c54de9a6ee50ada2bb904b5c7d00f9732c7
MD5 0f3a11444abfbd6d9e83c99fcfef7248
BLAKE2b-256 36f06b9d611b9cfb378b119b0133eba1c96ee79a6f83578ce1192809c196097a

See more details on using hashes here.

Provenance

The following attestation bundles were made for anaf_sync-0.2.0.tar.gz:

Publisher: release.yml on robert-malai/anaf-sync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anaf_sync-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: anaf_sync-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 100.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for anaf_sync-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7ad8986c7e45b5a17058e56921711cee429b96b417f6f9ff32a2b699a5d502be
MD5 5554b556eec1605a7e64f74d8e4d0cc8
BLAKE2b-256 e3329e427df1e01fcdc93e887237eb014f511f7b1d0c9c998effa0578ac4f81f

See more details on using hashes here.

Provenance

The following attestation bundles were made for anaf_sync-0.2.0-py3-none-any.whl:

Publisher: release.yml on robert-malai/anaf-sync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page