API -> "Copiaza tokenul" * set POLEO_CASA=DB9999999999 # NUI-ul casei (exemplul 2 ti-l arata) * * In PowerShell: * $env:POLEO_TOKEN=""; $env:POLEO_CASA=""; php poleo_demo.php --lista * * * ### DE CITIT INAINTE DE PRIMA LINIE DE COD IN PRODUCTIE ### * Bonul fiscal este foarte complicat de anulat si comporta constrangeri si consecinte. Orice * operatiune care lasa urma se trimite cu o CHEIE DE OPERATIUNE (`Idempotency-Key`) STABILA - * numarul comenzii tale, nu ceva generat la fiecare incercare. Exemplul 31 arata asta pe viu. * Nu trimite "probe" catre casa de marcat. Daca vrei sa verifici legatura, cere un raport X * (Atentie! NU Z). */ require __DIR__ . '/PoleoFiscal.php'; $poleo = PoleoFiscal::dinMediu(); $CASA = $poleo->casa(); // == afisare ====== function titlu($text) { echo "\n" . str_repeat('-', 74) . "\n" . $text . "\n" . str_repeat('-', 74) . "\n"; } function arata($eticheta, $obiect) { echo "\n" . $eticheta . ":\n"; echo json_encode($obiect, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n"; } function nota($text) { foreach (explode("\n", trim($text)) as $linie) { echo ' ' . trim($linie) . "\n"; } } /** * Cheie de operatiune pentru exemple. ATENTIE, aici este o ALTA cheie la fiecare rulare (are ora * in ea) - altfel al doilea rulaj al demonstratiei n-ar mai tipari nimic, ti-ar intoarce * rezultatul primului si ai crede ca s-a stricat ceva. IN PROGRAMUL TAU cheia trebuie sa fie * STABILA pentru aceeasi vanzare, ca reincercarea sa nu tipareasca al doilea bon. */ function cheie_demo($nume) { return sprintf('demo-%s-%d', $nume, (int) (microtime(true) * 1000)); } /** ruleaza ceva care poate fi refuzat, si arata refuzul in loc sa opreasca demonstratia */ function incearca($eticheta, callable $ce) { try { arata($eticheta, $ce()); } catch (EroareApi $e) { printf("\n%s -> HTTP %s (%s): %s\n", $eticheta, $e->cod, $e->codEroare ?: '-', $e->mesaj); } } // == 1-4: legatura si ce stie statia ====== function ex1_conectare(PoleoFiscal $p) { titlu('1. Conectarea la API'); arata('GET /health (singura cale care nu cere token)', $p->sanatate()); $metode = $p->metode(); $lista = isset($metode['methods']) ? $metode['methods'] : []; printf("\nMetodele pe care le stie ACEASTA statie (%d):\n", count($lista)); foreach ($lista as $m) { printf("%-6s %-28s %s\n", $m['method'] ?? '', $m['path'] ?? '', $m['about'] ?? ''); } nota('Cere /methods la pornirea integrarii tale, nu presupune. O statie mai veche poate sa nu aiba ultimele metode, iar asa afli din cod, nu dintr-o eroare la client.'); } function ex2_casele(PoleoFiscal $p) { titlu('2. Ce case sunt configurate (si de unde iei NUI-ul)'); arata('GET /cases', $p->casele()); nota('NUI-ul este identificatorul casei si nu se schimba pe toata durata vietii aparatului. Denumirea si id-ul din baza se pot schimba; NUI-ul, nu. Trimite-l de la inceput.'); } function ex3_stare(PoleoFiscal $p) { titlu('3. Starea casei, intrebata acum'); if ($p->casa() === '') { echo "(pune POLEO_CASA ca sa interoghezi o casa anume)\n"; } arata('GET /cases//status', $p->stare()); nota('Vorbeste chiar cu aparatul, deci intreaba la intervale rezonabile, nu in bucla si nu la fiecare incarcare de pagina. Starea monitorizarii si ziua fiscala vin din /problems.'); } function ex4_config(PoleoFiscal $p) { titlu('4. Cotele de TVA si metodele de incasare, citite din casa'); $r = $p->config(); arata('GET /cases//config', $r); $disp = $r['case']['device'] ?? []; echo "\nCote programate in aparat:\n"; foreach (($disp['vat'] ?? []) as $v) { printf("grupa %-3s = %s%%\n", $v['group'] ?? '?', $v['rate'] ?? '?'); } echo "Sloturi de incasare:\n"; foreach (($disp['payments'] ?? []) as $pl) { printf("index %-3s = %s\n", $pl['index'] ?? '?', $pl['name'] ?? '?'); } nota('Astea sunt adevarul. Nu presupune ca 21% este mereu grupa A sau ca 0 = NUMERAR, pentru ca vei altera datele fiscal-contabile.'); } // == 5-16: bonuri ====== function ex5_bon_numerar(PoleoFiscal $p) { titlu('5. Bon fiscal platit cu NUMERAR'); arata('POST /receipts', $p->bon([ 'items' => [['name' => 'APA PLATA 0.5L', 'price_val' => 3.50, 'qty' => 2, 'unit' => 'buc', 'vat_rate' => 21]], 'payments' => [['method' => 'cash', 'amount' => 7.00]], ], cheie_demo('numerar'))); nota('`receipt_no` este numarul bonului tiparit. Pune-l in evidenta ta - este legatura dintre vanzarea din programul tau si hartia pe care o are clientul.'); } function ex6_citeste_bonul(PoleoFiscal $p) { titlu('6. Citirea raspunsului: ultimul bon din evidenta'); $lista = $p->bonuri(1); arata('GET /receipts?limit=1', $lista); $bonuri = $lista['receipts'] ?? []; if ($bonuri) { arata('GET /receipts/' . $bonuri[0]['id'] . ' (cu liniile lui)', $p->bonul($bonuri[0]['id'])); } nota('Lista este pentru verificare ulterioara: reconciliere de sfarsit de zi, sau aflarea a ce s-a intamplat cu un bon intrat in coada cat timp casa era deconectata.'); } function ex7_bon_card(PoleoFiscal $p) { titlu('7. Bon fiscal platit cu CARDUL'); arata('POST /receipts', $p->bon([ 'items' => [['name' => 'CAFEA LA FILTRU', 'price_val' => 12.00, 'qty' => 1, 'unit' => 'buc', 'vat_rate' => 21]], 'payments' => [['method' => 'card', 'amount' => 12.00]], ], cheie_demo('card'))); nota('Asta TIPARESTE bonul; nu cere banii de pe card. Pentru terminalul bancar este /pos.'); } function ex8_plati_card(PoleoFiscal $p) { titlu('8. Citirea raspunsului pentru platile cu cardul'); arata('GET /card-payments?limit=5', $p->platiCard(5)); nota('Pastreaza `auth` si `rrn` - cu ele reconciliezi cu extrasul bancii. Plata INCERTA este cazul care conteaza: nu o relua automat, cardul poate fi deja debitat.'); } function ex9_bon_adaos(PoleoFiscal $p) { titlu('9. Bon cu ADAOS (majorare)'); arata('POST /receipts', $p->bon([ 'items' => [[ 'name' => 'LIVRARE URGENTA', 'price_val' => 20.00, 'qty' => 1, 'vat_rate' => 21, 'adjust_kind' => 'sur_pct', 'adjust_value' => 15, ]], 'payments' => [['method' => 'cash', 'amount' => 23.00]], ], cheie_demo('adaos'))); nota('`adjust_kind`: sur_val (lei) sau sur_pct (procent). `payments.amount` este totalul DUPA ajustare: 20 + 15% = 23,00.'); } function ex10_bon_reducere(PoleoFiscal $p) { titlu('10. Bon cu REDUCERE'); echo "\n--- a) CORECT: reducerea individualizata PE PRODUSUL redus ---\n"; arata('POST /receipts', $p->bon([ 'items' => [ ['name' => 'VIN ROSU 0.75L', 'price_val' => 45.00, 'qty' => 1, 'vat_rate' => 21, 'adjust_kind' => 'dis_pct', 'adjust_value' => 10], ['name' => 'PAINE DE CASA', 'price_val' => 8.00, 'qty' => 1, 'vat_rate' => 11], ], 'payments' => [['method' => 'cash', 'amount' => 48.50]], ], cheie_demo('reducere'))); nota('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. Reducerea pe tot bonul se sparge pe cote si mosteneste cota fiecarei linii.'); } function ex11_plati_multiple(PoleoFiscal $p) { titlu('11. Bon cu DOUA metode de plata'); arata('POST /receipts', $p->bon([ 'items' => [['name' => 'MENIU ZILEI', 'price_val' => 40.00, 'qty' => 1, 'vat_rate' => 11]], 'payments' => [['method' => 'cash', 'amount' => 15.00], ['method' => 'card', 'amount' => 25.00]], ], cheie_demo('mixt'))); nota('Suma platilor trebuie sa dea exact totalul bonului. Casa refuza altfel, si bine face.'); } function ex12_cote_multiple(PoleoFiscal $p) { titlu('12. Bon cu mai multe cote de TVA'); arata('POST /receipts', $p->bon([ 'items' => [ ['name' => 'PAINE', 'price_val' => 5.00, 'qty' => 1, 'vat_rate' => 11], ['name' => 'DETERGENT', 'price_val' => 24.00, 'qty' => 1, 'vat_rate' => 21], ['name' => 'AMBALAJ SGR', 'price_val' => 0.50, 'qty' => 1, 'vat_rate' => 0], ], 'payments' => [['method' => 'cash', 'amount' => 29.50]], ], cheie_demo('cote'))); nota('Garantia SGR merge pe cota 0 - nu este o vanzare, este o garantie returnabila.'); } function ex13_bon_complet(PoleoFiscal $p) { titlu('13. Bon complet: mai multe articole, ajustari, doua plati'); arata('POST /receipts', $p->bon([ 'items' => [ ['name' => 'PIZZA CAPRICCIOSA', 'price_val' => 38.00, 'qty' => 2, 'unit' => 'buc', 'vat_rate' => 11], ['name' => 'APA MINERALA 0.5L', 'price_val' => 6.00, 'qty' => 2, 'unit' => 'buc', 'vat_rate' => 21], ['name' => 'TAXA LIVRARE', 'price_val' => 12.00, 'qty' => 1, 'vat_rate' => 21, 'adjust_kind' => 'dis_val', 'adjust_value' => 2.00], ], 'payments' => [['method' => 'card', 'amount' => 80.00], ['method' => 'cash', 'amount' => 8.00]], ], cheie_demo('complet'))); nota('Asa arata un bon real. Verifica intotdeauna ca totalul platilor este cel calculat de tine DUPA ajustari - nu lasa casa sa te corecteze, ca nu o face.'); } function ex14_storno(PoleoFiscal $p) { titlu('14. Linie de STORNO in bonul deschis'); arata('POST /receipts', $p->bon([ 'items' => [ ['name' => 'BERE 0.5L', 'price_val' => 9.00, 'qty' => 2, 'vat_rate' => 21], ['name' => 'BERE 0.5L', 'price_val' => 9.00, 'qty' => 1, 'vat_rate' => 21, 'storno' => true], ], 'payments' => [['method' => 'cash', 'amount' => 9.00]], ], cheie_demo('storno'))); nota('Stornarea unei linii se face IN bonul care inca nu s-a inchis. Dupa ce bonul a iesit, nu mai este storno - este retur (exemplul 42).'); } function ex15_stil_coduri(PoleoFiscal $p) { titlu('15. Articole trimise pe COD, nu pe denumire'); arata('POST /receipts', $p->bon([ 'items' => [['code' => '1024', 'qty' => 3, 'vat_rate' => 21]], 'payments' => [['method' => 'cash', 'amount' => 30.00]], ], cheie_demo('cod'))); nota('Merge doar daca articolul EXISTA in nomenclatorul casei, cu pretul programat acolo. Vezi exemplul 20 pentru raportul de nomenclator.'); } function ex16_copie(PoleoFiscal $p) { titlu('16. Copia ultimului bon'); arata('POST /copy', $p->copie(cheie_demo('copie'))); nota('Copia se tipareste cu mentiunea de duplicat. Nu este un al doilea bon fiscal.'); } function ex17_void(PoleoFiscal $p) { titlu('17. Anularea bonului DESCHIS'); incearca('POST /void', function () use ($p) { return $p->anuleazaBonDeschis(cheie_demo('void')); }); nota('Doar bonul nefinalizat. Functioneaza si cu licenta expirata - este una dintre cele doua supape ale programului (cealalta este sertarul).'); } function ex18_probleme(PoleoFiscal $p) { titlu('18. Ce nu este in regula acum'); arata('GET /problems', $p->probleme()); nota('Asta pui in bara de stare a portalului: monitorizare, ziua fiscala si de cate ore este deschisa, hartie, legatura. Nu starea casei - aia vorbeste cu aparatul de fiecare data.'); } // == 19-30: rapoarte, numerar, ANAF, jurnal ====== function ex19_raport_x(PoleoFiscal $p) { titlu('19. Raport X (citire, fara sa inchida ziua)'); arata('POST /reports {kind: X}', $p->raport('X', [], cheie_demo('x'))); nota('X este singura "proba" acceptabila daca vrei sa verifici ca ai legatura cu aparatul.'); } function ex20_nomenclator(PoleoFiscal $p) { titlu('20. Raportul de nomenclator'); foreach (['nomenclator', 'plu', 'departments'] as $fel) { try { arata('POST /reports {kind: ' . $fel . '}', $p->raport($fel, [], cheie_demo('nom-' . $fel))); break; } catch (EroareApi $e) { printf("%s -> HTTP %s: %s\n", $fel, $e->cod, $e->mesaj); } } nota('Nu orice casa are toate felurile. Cere /methods (exemplul 1) ca sa stii ce accepta a ta.'); } function ex21_depunere(PoleoFiscal $p) { titlu('21. Depunere de numerar in sertar'); arata('POST /cash {kind: in}', $p->numerar('in', 200, 'Fond de rulaj', cheie_demo('dep'))); } function ex22_retragere(PoleoFiscal $p) { titlu('22. Retragere de numerar din sertar'); arata('POST /cash {kind: out}', $p->numerar('out', 150, 'Ridicare incasari', cheie_demo('retr'))); nota('Se tipareste pe casa. Deci: cheie de operatiune, ca la bonuri.'); } function ex23_sertar(PoleoFiscal $p) { titlu('23. Soldul de sertar'); arata('GET /drawer', $p->sertar()); } function ex24_totaluri(PoleoFiscal $p) { titlu('24. Totalurile zilei si cotele'); arata('GET /totals', $p->totaluri()); arata('GET /vat', $p->cote()); nota('Ziua fiscala incepe la ultimul raport Z, nu la ora 00:00. Doua zile calendaristice pot sta in aceeasi zi fiscala, si invers.'); } function ex25_anaf_stare(PoleoFiscal $p) { titlu('25. Starea raportarii ANAF'); arata('GET /anaf', $p->anaf()); } function ex26_raport_periodic(PoleoFiscal $p) { titlu('26. Raport periodic din memoria fiscala'); arata('POST /reports {kind: periodic}', $p->raport('periodic', [ 'from' => '01-08-2026', 'to' => '31-08-2026', 'detailed' => false, ], cheie_demo('periodic'))); nota('Datele se dau zi-luna-an, NU in ISO. Poate tipari zeci de secunde - vezi nota despre `max_execution_time` din PoleoFiscal.php.'); } function ex27_export_je(PoleoFiscal $p) { titlu('27. Export de jurnal electronic pentru ANAF'); arata('POST /anaf', $p->exportJe('01-08-2026', '31-08-2026', cheie_demo('je'))); } function ex28_lista_je(PoleoFiscal $p) { titlu('28. Salvarile de jurnal electronic'); $r = $p->je(3); arata('GET /je?limit=3', $r); $salvari = $r['exports'] ?? ($r['je'] ?? []); if ($salvari) { arata('GET /je/' . $salvari[0]['id'] . ' (fisierele salvarii)', $p->jeSalvare($salvari[0]['id'])); } } function ex29_descarca_je(PoleoFiscal $p) { titlu('29. Descarcarea unui fisier de jurnal (raspuns BINAR)'); $r = $p->je(1); $salvari = $r['exports'] ?? ($r['je'] ?? []); if (!$salvari) { echo "(nicio salvare de jurnal - ruleaza intai exemplul 27)\n"; return; } $id = $salvari[0]['id']; $fisiere = $p->jeSalvare($id)['files'] ?? []; if (!$fisiere) { echo "(salvarea nu are fisiere)\n"; return; } $nume = is_array($fisiere[0]) ? ($fisiere[0]['name'] ?? '') : $fisiere[0]; $octeti = $p->jeFisier($id, $nume); $unde = __DIR__ . DIRECTORY_SEPARATOR . $nume; file_put_contents($unde, $octeti); printf("%s: %d octeti -> %s\n", $nume, strlen($octeti), $unde); nota('Raspunsul este BINAR, nu JSON. Nu-l trece prin nicio functie de text si nu-l pune in json_decode - il scrii pe disc, sau il trimiti la browser cu antetele lui.'); } function ex30_raport_z(PoleoFiscal $p) { titlu('30. Raport Z (INCHIDE ZIUA FISCALA)'); $ziua = date('Y-m-d'); arata('POST /reports {kind: Z}', $p->raport('Z', [], 'z-' . $ziua)); nota('Cheia este ziua, nu ora: doua apasari in aceeasi zi nu dau doua rapoarte Z. Z-ul pune toate valorile pe 0. Nu-l pune pe un buton la indemana intr-un portal.'); } // == 31-32: idempotenta si erorile ====== function ex31_idempotenta(PoleoFiscal $p) { titlu('31. Cheia de operatiune, pe viu'); $cheie = cheie_demo('idem'); $corp = [ 'items' => [['name' => 'TEST IDEMPOTENTA', 'price_val' => 1.00, 'qty' => 1, 'vat_rate' => 21]], 'payments' => [['method' => 'cash', 'amount' => 1.00]], ]; arata('prima trimitere', $p->bon($corp, $cheie)); arata('A DOUA trimitere, cu ACEEASI cheie', $p->bon($corp, $cheie)); nota('Al doilea raspuns este acelasi ca primul, si NU a iesit al doilea bon. Asta te salveaza cand utilizatorul da F5 sau apasa de doua ori pe buton in portal.'); } function ex32_erori(PoleoFiscal $p) { titlu('32. Cum arata refuzurile, si pe ce te ramifici'); $cazuri = [ 'cerere fara articole' => function () use ($p) { return $p->bon(['items' => [], 'payments' => [['method' => 'cash', 'amount' => 1]]], cheie_demo('gol')); }, 'plata care nu acopera bonul' => function () use ($p) { return $p->bon([ 'items' => [['name' => 'X', 'price_val' => 10.00, 'qty' => 1, 'vat_rate' => 21]], 'payments' => [['method' => 'cash', 'amount' => 5.00]], ], cheie_demo('scurt')); }, 'raport care nu exista' => function () use ($p) { return $p->raport('nu-exista-asa-ceva', [], cheie_demo('rap')); }, ]; foreach ($cazuri as $ce => $f) { try { $f(); echo "\n" . $ce . ": a trecut (neasteptat)\n"; } catch (EroareApi $e) { printf("\n%-32s HTTP %-4s cod=%-16s %s\n", $ce, $e->cod, $e->codEroare ?: '-', $e->mesaj); } } nota('Ramifica-te pe `codEroare`, NU pe mesaj: codul este stabil, mesajul este pentru om si se poate schimba fara sa strice integrarea ta.'); } // == 33-41: sertar si terminalul de card ====== function ex33_sertar_deschide(PoleoFiscal $p) { titlu('33. Deschiderea sertarului'); incearca('POST /drawer', function () use ($p) { return $p->deschideSertar(cheie_demo('sertar')); }); nota('Necesita sertar legat electric la casa. Unele case nu-l pot gestiona, desi au port.'); } function ex34_pos_card(PoleoFiscal $p) { titlu('34. Plata la terminalul de card (POS bancar)'); incearca('POST /pos', function () use ($p) { return $p->pos(12.00, cheie_demo('pos')); }); nota('Asta CERE BANII de pe card; nu tipareste bonul. Intai /pos, si doar daca terminalul a aprobat, /receipts cu plata pe card. Programul nu vede niciodata PAN, CVV sau PIN.'); } function ex35_terminale(PoleoFiscal $p) { titlu('35. Terminalele de card configurate'); $r = $p->terminale(); arata('GET /terminals', $r); foreach (($r['terminals'] ?? []) as $t) { $poate = $t['can'] ?? []; printf( "%-22s %-16s retur: %-5s anulare: %-5s inchidere de zi: %s\n", $t['name'] ?? '-', $t['model'] ?? '-', var_export($poate['refund'] ?? null, true), var_export($poate['void'] ?? null, true), var_export($poate['settlement'] ?? null, true) ); } nota('Citeste `can` INAINTE sa promiti ceva clientului - se citeste din driver, nu de pe fir. Ce este `false` acolo nu se reincearca niciodata.'); } function ex36_licenta(PoleoFiscal $p) { titlu('36. Licenta fiecarei case'); arata('GET /license', $p->licenta()); nota('O licenta expirata opreste bonurile, rapoartele si miscarile de numerar. Afla inainte, nu in dimineata in care casa refuza.'); } function ex37_statie(PoleoFiscal $p) { titlu('37. Amprenta statiei, locatia si casele ei'); arata('GET /station', $p->statie()); nota('AMPRENTA este numele sub care te cunosc partenerii. Nu se schimba nici la actualizare, nici daca schimbi portul, IP-ul sau casa.'); } function ex38_restul_evidentei(PoleoFiscal $p) { titlu('38. Restul evidentei: numerar, rapoarte tiparite, declaratii, posta'); incearca('GET /cash?limit=3 (miscarile de numerar)', function () use ($p) { return $p->miscariNumerar(3); }); incearca('GET /reports?limit=3 (rapoartele tiparite)', function () use ($p) { return $p->rapoarte(3); }); incearca('GET /declarations?limit=3 (declaratiile de bon gresit)', function () use ($p) { return $p->declaratii(3); }); incearca('GET /emails?limit=3 (posta de iesire)', function () use ($p) { return $p->posta(3); }); $decl = $p->declaratii(1)['declarations'] ?? []; if ($decl) { $did = $decl[0]['id']; $pdf = $p->declaratieFisier($did); $unde = __DIR__ . DIRECTORY_SEPARATOR . 'declaratie-' . $did . '.pdf'; file_put_contents($unde, $pdf); printf("PDF-ul declaratiei %s: %d octeti -> %s\n", $did, strlen($pdf), $unde); } else { echo "(nicio declaratie de bon gresit - normal pe o casa fara incidente)\n"; } nota('Lista completa si mereu la zi o da chiar statia, la /methods - sau site-ul https://poleo.ro/docs/api'); } /* Retur, anulare si inchidere de zi pe terminal: cele trei nu exista pe orice terminal si nu sunt in orice contract cu banca. De aceea raspunsul lor are un caz in plus fata de "a mers / n-a mers": HTTP 501 cu `unsupported`, adica "aparatul asta nu are operatiunea". Este DEFINITIV.*/ function _card_operatie($nume, callable $ce) { try { arata($nume, $ce()); } catch (EroareApi $e) { if ($e->esteNesuportat()) { printf("\nNESUPORTAT de terminalul asta: %s\n", $e->mesaj); echo "Asta NU se reincearca - nici acum, nici maine. Solutia este alt terminal, alt\n"; echo "model, sau alt contract cu banca.\n"; if ($e->detalii) { arata('ce aparat a refuzat', $e->detalii); } } else { printf("\n%s -> HTTP %s (%s): %s\n", $nume, $e->cod, $e->codEroare ?: '-', $e->mesaj); } } } function ex39_retur_card(PoleoFiscal $p) { titlu('39. Retur pe card'); _card_operatie('POST /pos/refund', function () use ($p) { return $p->posRetur(25.00, cheie_demo('retur')); }); nota('Scoate bani REALI din contul comerciantului. Verifica `can.refund` la /terminals INAINTE, ca sa nu descoperi asta dupa ce clientul a predat marfa. Altceva decat exemplul 42: acolo este returul PE BON, aici returul PE TERMINAL.'); } function ex40_anulare_card(PoleoFiscal $p) { titlu('40. Anularea unei plati cu cardul'); _card_operatie('POST /pos/void', function () use ($p) { return $p->posAnulare('000123', 25.00, cheie_demo('anulare')); }); nota('Are nevoie de STAN SI de suma exacta, tocmai ca sa nu anulezi alta tranzactie. Pastreaza STAN-ul la fiecare plata, altfel nu mai ai cu ce anula.'); } function ex41_settlement(PoleoFiscal $p) { titlu('41. Inchiderea de zi a terminalului (settlement)'); _card_operatie('POST /pos/settlement', function () use ($p) { return $p->posInchidereZi(cheie_demo('settlement')); }); nota('NU are legatura cu raportul Z al casei - sunt doua zile diferite. Nu o facem noi automat: are efect asupra banilor, deci o cere omul.'); } // == 42-45: returul pe bon emis, documentele, firma, setarile ====== /** id-ul ultimului bon TIPARIT din evidenta - returul si declaratia lucreaza pe el */ function _ultimul_bon_tiparit(PoleoFiscal $p) { foreach (($p->bonuri(10)['receipts'] ?? []) as $b) { if (($b['status'] ?? '') === 'ok') { return $b['id']; } } return null; } function ex42_retur(PoleoFiscal $p) { titlu('42. Retur pe un bon deja emis'); $bid = _ultimul_bon_tiparit($p); if (!$bid) { echo "(niciun bon tiparit in evidenta - ruleaza intai exemplul 5)\n"; } else { incearca('POST /returns', function () use ($p, $bid) { return $p->retur($bid, 'produs defect, adus inapoi de client', 'demo'); }); } arata('GET /returns?limit=3', $p->retururi(3)); nota('NU se tipareste bon de storno: bonul initial ramane valabil. Ce iese este retragerea de numerar si procesul-verbal. Acelasi bon nu poate fi returnat de doua ori peste suma lui. `acknowledged` inseamna ca OMUL a decis - nu-l pune true dintr-un camp ascuns.'); } function ex43_declaratie(PoleoFiscal $p) { titlu('43. Declaratia de bon gresit'); arata('GET /declaration-reasons', $p->motiveDeclaratie()); $bid = _ultimul_bon_tiparit($p); $motive = array_slice(array_map( function ($m) { return $m['key'] ?? null; }, $p->motiveDeclaratie()['reasons'] ?? [] ), 0, 1); if ($bid && $motive) { incearca('POST /declarations', function () use ($p, $bid, $motive) { return $p->declaratie($bid, $motive, 'demo'); }); } nota('Motivele se iau din /declaration-reasons, nu se inventeaza. PDF-ul se descarca separat, ca raspuns binar (exemplul 38).'); } function ex44_firma(PoleoFiscal $p) { titlu('44. Datele firmei, asa cum le stie statia'); arata('GET /company', $p->firma()); nota('Sunt datele cu care se scriu documentele (procese-verbale, declaratii). Se configureaza PE CASA, nu global.'); } function ex45_setari(PoleoFiscal $p) { titlu('45. Setarile statiei: citire si scriere'); $r = $p->setari(); arata('GET /settings', $r); echo "\nCe se poate scrie prin API:\n"; foreach (($r['settings'] ?? []) as $s) { if (!empty($s['writable'])) { printf(" %-28s %s\n", $s['key'] ?? '', $s['secret'] ?? false ? '(secret - nu iese la citire)' : ''); } } nota('Parolele NU ies niciodata la citire: vin cu `value: null` si `secret: true`. Deci nu face un formular care "reincarca" parola existenta - n-o vei primi.'); echo "\n--- scrierea: una buna si una refuzata, in aceeasi cerere ---\n"; incearca('POST /settings', function () use ($p) { return $p->scrieSetari(['bon.sertar_mereu' => false, 'api.port' => '1']); }); nota('Tabul API in intregime este blocat DINADINS: API-ul nu isi poate schimba propriile conditii de existenta. Omul il porneste si il accepta din aplicatie, si abia dupa aceea este autorizat. Restul refuzurilor vin cu motiv, unul cate unul.'); // SFTP: acelasi drum, doar ca parola pleaca o singura data si nu se mai citeste inapoi. // arata('POST /settings (SFTP)', $p->scrieSetari([ // 'sftp.on' => true, 'sftp.host' => 'sftp.exemplu.ro', 'sftp.port' => '22', // 'sftp.user' => 'poleo', 'sftp.pass' => 'PAROLA', 'sftp.dir' => '/bonuri', // ])); } // == catalogul si rularea ====== $EXEMPLE = [ 1 => ['Conectarea la API', 'ex1_conectare', false], 2 => ['Casele configurate', 'ex2_casele', false], 3 => ['Starea casei', 'ex3_stare', false], 4 => ['Cotele de TVA si incasarile', 'ex4_config', false], 5 => ['Bon platit cu numerar', 'ex5_bon_numerar', false], 6 => ['Citirea unui bon din evidenta', 'ex6_citeste_bonul', false], 7 => ['Bon platit cu cardul', 'ex7_bon_card', false], 8 => ['Platile cu cardul', 'ex8_plati_card', false], 9 => ['Bon cu adaos', 'ex9_bon_adaos', false], 10 => ['Bon cu reducere', 'ex10_bon_reducere', false], 11 => ['Doua metode de plata', 'ex11_plati_multiple', false], 12 => ['Mai multe cote de TVA', 'ex12_cote_multiple', false], 13 => ['Bon complet', 'ex13_bon_complet', false], 14 => ['Storno in bonul deschis', 'ex14_storno', false], 15 => ['Articole pe cod', 'ex15_stil_coduri', false], 16 => ['Copia ultimului bon', 'ex16_copie', false], 17 => ['Anularea bonului deschis', 'ex17_void', false], 18 => ['Ce nu este in regula acum', 'ex18_probleme', false], 19 => ['Raport X', 'ex19_raport_x', false], 20 => ['Raport de nomenclator', 'ex20_nomenclator', false], 21 => ['Depunere de numerar', 'ex21_depunere', false], 22 => ['Retragere de numerar', 'ex22_retragere', false], 23 => ['Soldul de sertar', 'ex23_sertar', false], 24 => ['Totalurile zilei', 'ex24_totaluri', false], 25 => ['Starea ANAF', 'ex25_anaf_stare', false], 26 => ['Raport periodic', 'ex26_raport_periodic', false], 27 => ['Export de jurnal electronic', 'ex27_export_je', false], 28 => ['Salvarile de jurnal', 'ex28_lista_je', false], 29 => ['Descarcarea unui fisier de jurnal', 'ex29_descarca_je', false], 30 => ['Raport Z (INCHIDE ZIUA)', 'ex30_raport_z', true], 31 => ['Cheia de operatiune, pe viu', 'ex31_idempotenta', false], 32 => ['Cum arata refuzurile', 'ex32_erori', false], 33 => ['Deschiderea sertarului', 'ex33_sertar_deschide', false], 34 => ['Plata la terminalul de card', 'ex34_pos_card', false], 35 => ['Terminalele configurate', 'ex35_terminale', false], 36 => ['Licenta caselor', 'ex36_licenta', false], 37 => ['Amprenta statiei', 'ex37_statie', false], 38 => ['Restul evidentei', 'ex38_restul_evidentei', false], 39 => ['Retur pe card', 'ex39_retur_card', true], 40 => ['Anularea unei plati cu cardul', 'ex40_anulare_card', true], 41 => ['Inchiderea de zi a terminalului', 'ex41_settlement', true], 42 => ['Retur pe un bon emis', 'ex42_retur', true], 43 => ['Declaratia de bon gresit', 'ex43_declaratie', false], 44 => ['Datele firmei', 'ex44_firma', false], 45 => ['Setarile statiei', 'ex45_setari', false], ]; function lista(array $EXEMPLE) { echo "\nExemple disponibile:\n"; foreach ($EXEMPLE as $nr => $e) { printf("%3d. %-42s %s\n", $nr, $e[0], $e[2] ? ' [periculos - cere --da]' : ''); } echo "\nPericulos = lasa urma pe care nu o poti sterge: inchide ziua fiscala, misca bani reali.\n"; } // rulare doar din linia de comanda; daca fisierul ajunge sub un server web, nu executa nimic if (PHP_SAPI !== 'cli') { http_response_code(403); exit("poleo_demo.php se ruleaza din linia de comanda, nu din browser.\n"); } $argumente = array_slice($argv, 1); $da = in_array('--da', $argumente, true); $argumente = array_values(array_filter($argumente, function ($a) { return $a !== '--da'; })); if (!$argumente || in_array('--lista', $argumente, true)) { lista($EXEMPLE); exit(0); } $deRulat = in_array('toate', $argumente, true) ? array_keys($EXEMPLE) : array_map('intval', $argumente); foreach ($deRulat as $nr) { if (!isset($EXEMPLE[$nr])) { printf("Nu exista exemplul %s. Ruleaza --lista.\n", $nr); continue; } list($nume, $functie, $periculos) = $EXEMPLE[$nr]; if ($periculos && !$da) { printf("%3d. %-42s SARIT (periculos; adauga --da)\n", $nr, $nume); continue; } try { $functie($poleo); } catch (EroareApi $e) { printf("\nExemplul %d s-a oprit: HTTP %s (%s) - %s\n", $nr, $e->cod, $e->codEroare ?: '-', $e->mesaj); } }