Parsare factura TXT in obiect JSON

Salut,
Vreau să transform o factură PDF de la emag în JSON. Prima dată o transform din PDF în text folosind pdftotext:

pdftotext -layout -q -nopgbrk -enc UTF-8 -eol unix emag.pdf

Care imi generează următorul fișier: https://gist.githubusercontent.com/weaseldotro/15da9ddc97eeacba3411d33ea1d27baa/raw/d940cd9d14ffe9bd2b9ae5a7b5db5d828497d42b/factura%20emag.txt

Am nevoie de cineva să-mi scrie un program care să parseze fișierul ăsta și să genereze un obiect JSON care să arate așa:

{
    "data": "2020-12-20",
    "serie": "ROWH",
    "numar": "219912150000",
	"cumparator": {
		"denumire": "FIRMA DEMO SRL",
		"nrRegCom": "J38/927/2003",
		"cif": "RO12345678",
		"sediu": "str. Tineretului nr. 1",
		"judet": "Alba",
		"cont": "RO12",
		"banca": "UNICREDIT"
	},
	"articole": [
		{
			"denumire": "Procesor AMD Ryzen™ 9 5900X, 64MB, 4.8GHz, Socket AM4",
			"cod": "100-100000061WOF",
			"um": "buc",
			"cantitate": 1,
			"pretUnitar": 2941.17,
			"cotaTVA": 19,
			"valoare": 2941.17,
			"valoareTVA": 558.82
		},
		{
			"denumire": "Pasta termica Deepcool Z3, 1.5 g",
			"cod": "DP-Z3",
			"um": "buc",
			"cantitate": 1,
			"pretUnitar": 11.36,
			"cotaTVA": 19,
			"valoare": 11.36,
			"valoareTVA": 2.16
		},
		{
			"denumire": "Discount conform promotie: eMAG-100-100000061WOF",
			"cod": "DPRO1VAT20TYPE11",
			"um": "buc",
			"cantitate": 1,
			"pretUnitar": -691.25,
			"cotaTVA": 19,
			"valoare": -691.25,
			"valoareTVA": -131.34
		}
	]
}

Programul trebuie să fie scris musai în limbajul de programare go (golang) și să aibă licență “unlicense” (The Unlicense | Choose a License) sau “wtfpl” (Do What The F*ck You Want To Public License | Choose a License). Codul îl puteți posta pe github sau aici sau unde vreți, și va fi public și open source după ce îl recepționez.

Preț: spuneți-mi voi. Modalitate de plată: transfer in cont bancar.
Freelance. Remote.
Contact: mesaj privat

De ce vrei sa dai bani, cand acea aplicatie o sa fie open source?
Doar intreb.

1 Like

Dau bani fiindcă cineva muncește ca să-l facă. Open source ca să-l mai folosească și altcineva dacă are nevoie.

10 Likes

Nu asa se face. Trebuie un parser de pdf nu unul txt, si nici asa nu e usor

daca garantezi ca formatul sursei txt nu se schimba niciodata, sunt sanse sa gasesti
altfel…slabe sanse

Păi mie îmi trebuie pentru PDF-urile generate de emag din momentul ăsta, dacă formatul se modifică pe viitor atunci o să-l adaptez.

Un proiect interesant pentru un junior :slight_smile: Go for it, se poate invata golang din asta.

Niste instructiuni de implementare:

  1. Va scrieti un generator de facturi in format PDF identic cu ce genereaza emag in golang.
  2. Scrieti un test care sa verifice ca output-ul programului vostru contine tot ce trebuie sa contina JSON-ul dupa conversia pdf-ului.
  3. Scrieti parserul dupa o schema de pe PDF care sa treaca testele pe care le scrieti cu 1 si 2.

Problema cu pdftotext e ca spatiul din PDF poate nu va aparea ca spatiu in text si poate nu mai iese bine parsarea.

E o problema mai complexa decat pare fiindca PDF-ul nu se citeste usor si textul generat cu pdf2text poate nu are spatiile constante. Trebuie luate in considerare si edge case-urile cu reduceri, cadouri, vouchere care apar intre produse.

3 Likes

Nu trebuie scris nici un generator, se pot folosi câteva facturi deja generate. Nu trebuie parsat fișierul PDF, ci fișierul TXT. Nu înțeleg de ce tot primesc răspunsuri că trebuie parsat direct PDF-ul (voi ați parsat vreodata un PDF?), când fișierele PDF sunt niște documente care nu au nici un fel de structură. Și o zic oameni care cunosc bine subiectul: java - Extract columns of text from a pdf file using iText - Stack Overflow

Așa că soluția cea mai simplă este asta cu pdftotext.

2 Likes

Ce vrea @kleampa să spună cu formatul sursei este că la unele facturi mai lipsesc date. E.g. Poate una are IBAN, poate alta nu :slight_smile:

All in all, nu mi se pare imposibil de făcut; m-aș lua de faptul că blocul de text din dreapta începe de la coloana ~135, blocul din stânga de la coloana 45, pe când rândurile cu produse încep de la coloana 37 etc.

Dar am impresia că vor fi o grămadă de edge case-uri :smiley:

2 Likes

Nu aș folosi niciodata valori / poziționare absolută la parsarea textului, fiindcă astea e cel mai posibil să se schimbe.

Aș merge pe căutarea de keywords / markers precum Cumparator(denumire, forma juridica) sau Nr. de inmatriculare in Registrul Comertului + condiția ca textul să nu fie la începutul liniei, ca să iau datele de la cumpărător, nu furnizor.

Pentru articole e și mai simplu, pornesc de la Denumirea produselor sau a serviciilor, ignor următoarele linii și apoi parsez fiecare linie. Trim spaces, dacă linia începe cu un număr atunci este vorba de un articol nou, altfel e continuare de la același articol. Iar coloanele se distringe rapid deoarece sunt separate de 2 sau mai multe spații și sunt mereu aceleași.

Pentru cineva care știe ce face nu ar trebui să dureze mai multe de 60 minute.

Regex to the rescue

1 Like

E mai riscant cu keyword-uri, exemplul dat de tine e simplu, dar pot fi facturi mult mai complexe pe 2 sau mai multe pagini, cu cadouri, cu vouchere, cu stornare, cu date lipsa.

Nu stiu de ce ma gandeam ca e mai simplu sa citesti pdf-ul caracter cu caracter dupa font, daca il convertesti intr-adevar salvezi spatiu si e mai simplu.

Regex-ul nu e o solutie optima mai niciodata, daca poti scrie cu for-uri ce trebuie o sa fie mult mai rapid.

Paternul asta l-am vazut de atatea ori in viata reala:
Organizatia X are o echipa care transforma un obiect intr-un document, organizatia Y are o echipa care din document extrage un obiect, de multe ori cu erori.
In afara valorii educationale si a faptului ca se da de munca la doua echipe care adauga valoare si contribuie la PIB, e o risipa de resurse. Suna la emag sa-ti dea obiectul JSON de care ai nevoie! Probabil au deja un API.

3 Likes

Slabe șanse să-ți dea ăia așa ceva, chiar dacă au. Și eu vreau să parsez facturile emag pentru oricare client, nu doar ale mele. De asemenea, după emag vreau să implementez mecanismul și pentru alți furnizori mari, spre exemplu Orange, Vodafone, RDS, Altex, Flanco, etc.

1 Like

Salut,

Daca formatul nu se schimba, atunci se poate folosi Regex, daca e ceva ce se schimba destul de frecvent doar ML/AI.

Pai daca e asa simplu de ce nu il faci tu? :slightly_smiling_face:
However, spre deosebire de convertorul de la pdf care probabil a lucrat pe linie indiferent ce era acolo tu va trebui sa aduci informatia aia in campuri si valori foarte precise.
Asta e mult mai complicat in realitate. Vad acolo denumirea produselor pe mai multe linii care influenteaza si pozitia campurilor din alte coloane, headerul la fel, la unele produse ai discount la altele nu ai, tva-urile se mai schimba (au mai fost situatii, mai multe cote?), alte adaugiri de care nu sti si care pe factura aia nu sunt dar pot aparea in alte situatii iar asta te va duce la probleme si modificari continue.
Asa cum a mai spus cineva mai sus trebuie sa fie un API pentru asta. Parsarea nu este o solutie in acest caz.

1 Like

Doar pentru incurajare:

Am avut cu ceva vreme in urma de parsat foarte multe pdf-uri de la diversi fabricanti (camere video pentru surveillance, pentru cine e curios).
Pe o solutie in genul ala am mers si eu, pdftotext apoi parsare de text (fisierele contineau o gramada de imagini, separat am scos informatie chiar si din alea, dar asta e o alta poveste).
A fost regex la greu plus o gramada de euristici ca sa localizez si extrag informatia relevanta afara din gunoi (erau multe povesti/reclama pe acolo, nu numai caracteristicile care interesau).
La momentul ala am ales perl pentru implementare.
A functionat neasteptat de bine, chiar cand au fost adaugate fisiere pdf de la alti fabricanti.

Treaba cu facturile pare simpla prin comparatie.

1 Like

<sarcasm>
WHAT? Nu ai folosit tensorflow pentru machine learning ca să parsezi direct PDF-ul folosind microservices într-un cluster kubernetes în aws multi region cu auto-scaling?
Amator nene…
</sarcasm>

Încă valabil, pentru cine e interesat de ceva simplu :slight_smile:

2 Likes

:slight_smile:

Adevarul e ca sunt de-aia care sar la machine learning si cand e sa rezolve o ecuatie de gradul doi.

No free lunch in search and optimization - Wikipedia

2 Likes

Încă valabil.