/* POLEO FISCAL - clientul C. Perechea in C a lui `poleo_demo.py` si `PoleoFiscal.php`: acelasi API, aceleasi metode, aceleasi capcane. Pentru programul de vanzari scris in C sau C++ care leaga o casa de marcat prin Poleo Fiscal. FARA DEPENDINTE PE WINDOWS. Transportul merge pe WinHTTP, care vine cu sistemul: gcc -std=c99 poleo_fiscal.c programul_tau.c -lwinhttp -o programul_tau.exe cl /W3 poleo_fiscal.c programul_tau.c winhttp.lib Pe Linux sau macOS se compileaza pe libcurl: cc -std=c99 -DPOLEO_CU_CURL poleo_fiscal.c programul_tau.c -lcurl -o programul_tau Din C++ merge direct, fisierul are `extern "C"`. ### DE CITIT INAINTE DE PRIMA LINIE DE COD IN PRODUCTIE ### Bonul fiscal (inscrisul tiparit) este foarte complicat de anulat si comporta constrangeri si consecinte. De aceea, orice operatiune care lasa urma se trimite cu o CHEIE DE OPERATIUNE (antetul `Idempotency-Key`): daca reteaua cade dupa ce bonul a iesit si programul tau reincearca, cheia opreste al doilea bon si iti intoarce rezultatul primului. Cheia trebuie sa fie a TA si STABILA pentru aceeasi vanzare - numarul comenzii, al bonului din gestiune. NU folosi ora, un contor de incercari sau un numar aleator: atunci fiecare reincercare ar fi alta operatiune si ar iesi al doilea bon. Nu trimite "probe" catre casa de marcat. Daca vrei sa verifici legatura, cere un raport X (Atentie! NU Z, acela inchide ziua fiscala). */ #ifndef POLEO_FISCAL_H #define POLEO_FISCAL_H #include #ifdef __cplusplus extern "C" { #endif /* Casa poate tipari zeci de secunde la un raport din memoria fiscala. Un timeout de 5 secunde, obisnuit in clientii HTTP, ar taia exact operatiunile lungi - si ai crede ca au esuat, desi ele continua la casa. */ #define POLEO_TIMEOUT_SCURT 15 #define POLEO_TIMEOUT_LUNG 180 /* == statia ==================================================================================== * Structura se completeaza pe loc; nu aloca nimic si nu trebuie eliberata. * * baza - adresa statiei, ex. "http://127.0.0.1:9111" * token - Setari -> API -> "Copiaza tokenul" * casa - NUI-ul casei. Gol = statia alege casa ei obisnuita; ca good practice nu recomandam * asta, ci identificarea dupa NUI (id-ul unic al fiecarui aparat fiscal, similar cu * seria de sasiu a unei masini). Denumirea si id-ul din baza se pot schimba; NUI-ul, * nu - este acelasi pe toata durata vietii aparatului. */ typedef struct { char baza[256]; char token[512]; char casa[64]; } poleo_t; /* completeaza din variabilele de mediu POLEO_URL / POLEO_TOKEN / POLEO_CASA, cu valori implicite */ void poleo_din_mediu(poleo_t *p); void poleo_init(poleo_t *p, const char *baza, const char *token, const char *casa); /* == raspunsul ================================================================================= * Un singur tip pentru "a mers" si pentru "a fost refuzat", ca sa nu ai doua drumuri prin cod. * * ok - 1 daca statia a raspuns cu 2xx * cod - statusul HTTP; 0 inseamna ca NU am ajuns la statie (retea, program oprit) * cod_eroare - codul STABIL din raspuns ("bad_request", "device_error", "unsupported" ...); * pe ASTA te ramifici in cod, nu pe mesaj: mesajul este pentru om si se poate * schimba fara sa strice integrarea ta * mesaj - propozitia pentru om * corp - raspunsul brut, terminat cu '\0'. Poate fi JSON sau BINAR (PDF-uri, fisiere de * jurnal); pentru binar foloseste `corp_lung`, nu strlen * * DUPA FIECARE APEL se cheama poleo_elibereaza(), inclusiv cand ok == 0. */ typedef struct { int ok; int cod; char cod_eroare[64]; char mesaj[512]; char *corp; size_t corp_lung; } poleo_raspuns_t; void poleo_elibereaza(poleo_raspuns_t *r); /* 501 + `unsupported` inseamna "aparatul asta nu are operatiunea". Este un raspuns DEFINITIV: nu se reincearca niciodata, nici acum nici maine. Solutia este alt terminal, alt model, sau alt contract cu banca. */ int poleo_nesuportat(const poleo_raspuns_t *r); /* == cererea de baza =========================================================================== `cheie` devine antetul `Idempotency-Key`; NULL daca operatiunea nu lasa urma. `corp_json` este trupul cererii, deja serializat; NULL pentru GET. Intoarce 1 daca a mers (acelasi lucru ca r->ok). */ int poleo_cere(poleo_t *p, const char *metoda, const char *cale, const char *corp_json, const char *cheie, int timeout, poleo_raspuns_t *r); /* == citirea raspunsului ======================================================================= NU este un parser JSON complet, si nici nu incearca sa fie. Scoate un camp dupa nume, sarind corect peste siruri, paranteze si escapari. Cauta PRIMA aparitie a numelui, la orice adancime. Este exact cat iti trebuie ca sa iei `receipt_no`, `id`, `status` sau un total. Daca ai de umblat prin liste (nomenclatoare, liste de bonuri), ia `r->corp` si da-l unui parser adevarat - cJSON, jansson, ce folosesti deja. Nu-ti impunem noi o biblioteca. poleo_text - copiaza valoarea ca text, cu escaparile desfacute; intoarce 1 daca a gasit poleo_numar - valoarea ca double; intoarce 1 daca a gasit poleo_adevarat - true/false; intoarce 1 daca a gasit */ int poleo_text(const poleo_raspuns_t *r, const char *nume, char *afara, size_t marime); int poleo_numar(const poleo_raspuns_t *r, const char *nume, double *afara); int poleo_adevarat(const poleo_raspuns_t *r, const char *nume, int *afara); /* == constructorul de bon ====================================================================== * Bonul are articole si plati. Se construieste in pasi, se trimite, se elibereaza. * * poleo_bon_t *b = poleo_bon_nou(); * poleo_bon_articol(b, "APA PLATA 0.5L", 3.50, 2, "buc", 21); * poleo_bon_plata(b, "cash", 7.00); * poleo_tipareste(&p, b, "comanda-10471", &r); * poleo_bon_elibereaza(b); * * Suma platilor trebuie sa dea exact totalul bonului DUPA ajustari. Casa refuza altfel, si bine * face - nu te corecteaza ea. */ typedef struct poleo_bon poleo_bon_t; poleo_bon_t *poleo_bon_nou(void); void poleo_bon_elibereaza(poleo_bon_t *b); /* un articol obisnuit; `um` poate fi NULL ("buc" implicit la casa) */ void poleo_bon_articol(poleo_bon_t *b, const char *nume, double pret, double cant, const char *um, int cota_tva); /* * Articol cu ajustare pe randul lui. `fel`: * "dis_val" reducere in lei "dis_pct" reducere procentuala * "sur_val" majorare in lei "sur_pct" majorare procentuala * * Individualizeaza EXPRES produsul redus, fara exceptie. Are implicatii nu doar fiscal-contabile, ci si juridice: clientul trebuie sa vada din ce s-a compus suma. */ void poleo_bon_articol_ajustat(poleo_bon_t *b, const char *nume, double pret, double cant, const char *um, int cota_tva, const char *fel, double valoare); /* articol trimis pe COD; merge doar daca exista in nomenclatorul casei, cu pretul programat acolo */ void poleo_bon_articol_cod(poleo_bon_t *b, const char *cod, double cant, int cota_tva); /* linie de STORNO in bonul care inca nu s-a inchis; dupa ce bonul a iesit, este retur, nu storno */ void poleo_bon_storno(poleo_bon_t *b, const char *nume, double pret, double cant, int cota_tva); /* `metoda`: "cash" sau "card". "card" trece suma pe slotul de card al casei; NU cere banii de pe * card - pentru terminalul bancar este poleo_pos(). */ void poleo_bon_plata(poleo_bon_t *b, const char *metoda, double suma); /* trupul JSON construit pana acum, pentru depanare; ramane valabil pana la urmatoarea adaugare */ const char *poleo_bon_json(poleo_bon_t *b); /* == metodele API ============================================================================== Ordinea si numele urmeaza fisierele Python si PHP, ca sa poti citi orice exemplu in oricare dintre cele trei limbaje.*/ /* 1-4: legatura si ce stie statia */ int poleo_sanatate(poleo_t *p, poleo_raspuns_t *r); /* singura cale care nu cere token */ int poleo_metode (poleo_t *p, poleo_raspuns_t *r); /* ce stie CHIAR statia asta; cere-le la pornire, nu presupune */ int poleo_casele (poleo_t *p, poleo_raspuns_t *r); int poleo_stare (poleo_t *p, const char *nui, poleo_raspuns_t *r); /* vorbeste cu aparatul: nu in bucla */ int poleo_config (poleo_t *p, const char *nui, poleo_raspuns_t *r); /* cotele si sloturile REALE ale casei */ /* bonuri */ int poleo_tipareste (poleo_t *p, poleo_bon_t *b, const char *cheie, poleo_raspuns_t *r); int poleo_bonuri (poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_bonul (poleo_t *p, const char *id, poleo_raspuns_t *r); int poleo_plati_card (poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_copie (poleo_t *p, const char *cheie, poleo_raspuns_t *r); int poleo_anuleaza_bon_deschis(poleo_t *p, const char *cheie, poleo_raspuns_t *r); int poleo_probleme (poleo_t *p, poleo_raspuns_t *r); /* rapoarte; `fel`: "X", "Z", "periodic", "nomenclator". `optiuni_json` = campuri in plus, NULL daca nu ai */ int poleo_raport (poleo_t *p, const char *fel, const char *optiuni_json, const char *cheie, poleo_raspuns_t *r); int poleo_rapoarte(poleo_t *p, int limit, poleo_raspuns_t *r); /* numerar si sertar; `fel`: "in" depunere, "out" retragere */ int poleo_numerar (poleo_t *p, const char *fel, double suma, const char *motiv, const char *cheie, poleo_raspuns_t *r); int poleo_miscari_numerar(poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_sertar (poleo_t *p, poleo_raspuns_t *r); int poleo_deschide_sertar(poleo_t *p, const char *cheie, poleo_raspuns_t *r); /* totaluri, TVA, ANAF, jurnal electronic; datele se dau zi-luna-an ("01-08-2026"), NU in ISO */ int poleo_totaluri (poleo_t *p, poleo_raspuns_t *r); int poleo_cote (poleo_t *p, poleo_raspuns_t *r); int poleo_anaf (poleo_t *p, poleo_raspuns_t *r); int poleo_export_je(poleo_t *p, const char *de_la, const char *pana_la, const char *cheie, poleo_raspuns_t *r); int poleo_je (poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_je_salvare(poleo_t *p, const char *id, poleo_raspuns_t *r); int poleo_je_fisier(poleo_t *p, const char *id, const char *nume, poleo_raspuns_t *r); /* BINAR: foloseste corp_lung */ /* statia, licenta, terminalele */ int poleo_licenta (poleo_t *p, poleo_raspuns_t *r); int poleo_statie (poleo_t *p, poleo_raspuns_t *r); int poleo_terminale(poleo_t *p, poleo_raspuns_t *r); /* `can` spune ce ARE fiecare terminal; citeste-l INAINTE */ /* terminalul de card. Programul nu vede niciodata numarul cardului, CVV-ul sau PIN-ul: trimite o * suma si primeste un rezultat cu RRN, cod de autorizare si STAN. */ int poleo_pos (poleo_t *p, double suma, const char *cheie, poleo_raspuns_t *r); int poleo_pos_retur (poleo_t *p, double suma, const char *cheie, poleo_raspuns_t *r); int poleo_pos_anulare (poleo_t *p, const char *stan, double suma, const char *cheie, poleo_raspuns_t *r); int poleo_pos_inchidere_zi(poleo_t *p, const char *cheie, poleo_raspuns_t *r); /* returul pe bon emis: NU se tipareste bon de storno; bonul initial ramane valabil, iar ce iese * este retragerea de numerar si procesul-verbal care justifica banii scosi din sertar */ int poleo_retur (poleo_t *p, const char *bon_id, const char *motiv, const char *cerut_de, const char *cheie, poleo_raspuns_t *r); int poleo_retururi(poleo_t *p, int limit, poleo_raspuns_t *r); /* declaratia de bon gresit; motivele se iau din poleo_motive_declaratie(), nu se inventeaza */ int poleo_motive_declaratie(poleo_t *p, poleo_raspuns_t *r); int poleo_declaratie (poleo_t *p, const char *bon_id, const char *motiv_cheie, const char *operator_, const char *cheie, poleo_raspuns_t *r); int poleo_declaratii (poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_declaratie_fisier(poleo_t *p, const char *id, poleo_raspuns_t *r); /* BINAR (PDF) */ /* posta, firma, setari */ int poleo_posta (poleo_t *p, int limit, poleo_raspuns_t *r); int poleo_firma (poleo_t *p, poleo_raspuns_t *r); int poleo_setari(poleo_t *p, poleo_raspuns_t *r); /* parolele NU ies: vin cu value null si secret true */ int poleo_scrie_setari(poleo_t *p, const char *setari_json, poleo_raspuns_t *r); #ifdef __cplusplus } #endif #endif /* POLEO_FISCAL_H */