Versiunea 0.6.0 a ajuns pe PyPI, dar nu a primit niciodată o pagină de release
și nici programe descărcabile: pasul care le construiește a picat pe Windows,
după publicare — a doua oară după 0.5.0, din alt motiv. Conținutul ei e inclus
mai jos, iar 0.6.1 e versiunea completă. Dacă ai instalat deja 0.6.0 de pe
PyPI, ai tot ce e descris la Îmbunătățiri; îți lipsește doar remedierea de la
Remedieri, care privește aplicația de pe Windows.
Îmbunătățiri
Facturile care apar cu unknown se pot repara. Se poate întâmpla ca
descărcarea să reușească, dar citirea XML-ului să nu: ANAF acceptă un document
pe care versiunea instalată de anafpy nu îl poate încă interpreta. Factura
se arhivează oricum — ZIP-ul semnat e scopul — dar tot ce se deduce din XML se
prăbușește în unknown: numărul, data, partenerul și valoarea din fereastra
Facturi, plus calea pe disc, care devine un folder unknown.
Până acum nu exista cale de întoarcere. Evidența nu revizitează niciodată un
mesaj deja arhivat — regula care garantează că nimic nu se descarcă de două
ori — iar după 60 de zile nici măcar sync --redownload nu mai ajunge la el.
Rândul rămânea greșit definitiv, oricât de mult ar fi învățat anafpy între
timp să citească.
Dovada nu s-a pierdut însă niciodată: XML-ul facturii stă permanent în ZIP-ul
arhivat. Exact pe asta se bazează și repararea PDF-urilor din 0.5.1, doar că
acum se aplică datelor și căii, nu randării:
anaf-sync reprocess --dry-run # arată ce s-ar corecta
anaf-sync reprocess # recitește ZIP-urile și corectează catalogul
anaf-sync reprocess --move --dry-run # și unde s-ar muta fișierele
anaf-sync reprocess --move # le mută pe calea dată de șablonFără --move se schimbă doar ce se vede în aplicație; fișierele rămân exact
unde sunt. Cu --move se recalculează și calea din șablon, se mută acolo toate
fișierele facturii și se șterge folderul unknown rămas gol. Dacă o factură
tot nu poate fi citită, comanda o numără separat, pe unreadable: atunci
merită raportată, iar o versiune mai nouă de anafpy plus încă o rulare a
aceleiași comenzi rezolvă restul.
Comanda nu cere autentificare și nu atinge rețeaua deloc, deci o arhivă veche
de ani se repară la fel de bine ca una de azi.
Un buton pentru fiecare factură, în fereastra Facturi. Panoul din dreapta
are acum „Recitește din arhivă” — aceeași operație, pentru o singură
factură. Pe rândurile cărora le lipsesc toate datele apare evidențiat, sub o
explicație a motivului: descărcarea a reușit, doar citirea nu, iar originalul
semnat e intact pe disc. Pe celelalte rânduri rămâne discret, ca să poți
re-așeza o factură după ce schimbi șablonul, fără să reorganizezi toată arhiva.
Facturile catalogate cu backfill au butonul dezactivat, cu motivul în tooltip:
sunt foldere pe care anaf-sync nu le administrează, așa că nu le rescrie
fișierele.
reprocess --move e și modul corect de a reorganiza arhiva după ce schimbi
template. Rearanjează local fișierele deja descărcate, spre deosebire de
sync --redownload, care poate aduce din nou doar ce se mai află în fereastra
de 60 de zile. Rulează întâi cu --dry-run, ca să vezi câte fișiere s-ar muta.
Remedieri
Pe Windows, aplicația nu mai rămâne blocată pe „Se sincronizează…”. Dacă
pornirea comenzii eșua — de pildă pentru că programul anaf-sync nu se mai
găsea acolo unde fusese instalat — meniul rămânea dezactivat definitiv, iar
butonul de reparare din fereastra Facturi la fel: aplicația aștepta un proces
care nu pornise niciodată, așa că nimic nu mai venea să o deblocheze. Singura
ieșire era repornirea aplicației.
Windows anunță o pornire eșuată pe loc, spre deosebire de macOS și Linux, care
o anunță o clipă mai târziu; aplicația trata cele două anunțuri în ordinea
greșită. Acum ordinea e garantată pe toate sistemele, iar un eșec de pornire
readuce meniul la loc, ca oricare altă rulare încheiată.
Internal
- What a re-projection may not invent.
message_typeandcreated_at
(data_creare, which the delay flag reads) come only from ANAF's listing,
which is long gone by the time anything is reprocessed — so the stored row
keeps them, along withcif,direction,saved_atand the message id.
Re-deriving them asNonewould silently turn "unknown delay" into "on
time".context.project_archivedis the third door onto the module's single
parse, carrying exactly those facts back in. request_idrefuses the move. It is the one path variable with no home
in any artifact, so a template referencing it errors up front rather than
renderingunknownover paths that already hold the real value. The catalog
refresh has no such dependency and still runs.- Moves are planned before they are applied. An occupied destination
refuses that message (its catalog is still refreshed); a destination whose
source is gone is recognised as a move a previous run already made; and the
file the pass re-read the message from moves last, so an interrupted run
resumes. Files land before the row does — a crash between them leaves a
stale pointer with every byte intact, where the reverse would point the row
at files that never arrived. SyncRunneris nowCliRunner, with one in-flight guard across every
subcommand the tray spawns. Two tray-spawned children would meet at the sync
lock and the second would die on it, so the UI does not offer the second
click.ARTIFACT_SUFFIXESmoves toconfig, shared bybackfilland
reprocessinstead of copied.CliRunneremitsstartedbefore it callsQProcess.start(). Windows
fails a launch insidestart(); POSIX defers the same error to the event
loop. Emitting afterwards deliveredfinishedbeforestartedon Windows
only, and the tray's two handlers then ran in the order that leaves the UI
disabled forever. This is what broke 0.6.0's release: the tray suite — which
0.5.1 had just started running on Windows in CI, precisely to stop finding
these at release time — caught it, but on the release job's leg, after the
publish step had already put 0.6.0 on PyPI.- The tray runner's tests fake at the
QProcessseam. They used to aim at
a missing executable and assert the in-flight guard still held, which is only
true where the failure is deferred — a test that encoded one platform's
timing as the contract. A fake process makes the guard's lifetime the
runner's own, and a variant that fails synchronously pins the signal order on
every platform.
⚠️ Verificare. Comandareprocessare teste peste facturi UBL reale
(recitire, mutare, mutare întreruptă și reluată, destinație ocupată, arhive
fără ZIP) și a fost rulată cap-coadă pe o arhivă de probă: factura a ieșit din
unknown, folderul gol a dispărut, catalogul s-a completat și a doua rulare
nu a mai avut ce face. Nu a fost însă rulată pe o arhivă reală de
producție — dacă--movese poartă altfel pe un volum de sute de facturi,
spune. Rulează întâi--dry-run, și ai oricând ZIP-urile semnate ca sursă de
adevăr. Blocajul de pe Windows are un test care reproduce eșecul sincron, dar
nu a fost verificat pe un Windows real — dacă meniul tot rămâne blocat
după o pornire eșuată, spune.
⚠️ The bundles are unsigned — no macOS notarization, no Windows
Authenticode. First run triggers the usual OS warning; use the
right-click-open workaround documented in the README. The CLI on PyPI
(pip install anaf-sync) is unaffected.
Full changelog: v0.6.0...v0.6.1