/* POLEO FISCAL - clientul C, implementarea. Interfata si explicatiile sunt in `poleo_fiscal.h`. Windows: gcc -std=c99 poleo_fiscal.c programul_tau.c -lwinhttp -o programul_tau.exe altundeva: cc -std=c99 -DPOLEO_CU_CURL poleo_fiscal.c programul_tau.c -lcurl Nu are alte dependinte. Pe Windows transportul este WinHTTP, care vine cu sistemul; nu am pus libcurl fiindca un program de vanzari nu trebuie sa care un DLL in plus doar ca sa tipareasca un bon in propria lui retea. */ #if defined(_WIN32) && !defined(POLEO_CU_CURL) # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0600 # endif # include # include # define POLEO_WINHTTP 1 #else # include # define POLEO_CURL 1 #endif #include #include #include #include "poleo_fiscal.h" /* == sir care creste singur ==================================================================== */ typedef struct { char *d; size_t n; /* octeti folositi, fara '\0' */ size_t cap; } sir_t; static int sir_loc(sir_t *s, size_t inca) { size_t nou; char *p; if (s->n + inca + 1 <= s->cap) return 1; nou = s->cap ? s->cap : 256; while (nou < s->n + inca + 1) nou *= 2; p = (char *) realloc(s->d, nou); if (!p) return 0; s->d = p; s->cap = nou; return 1; } static void sir_adauga(sir_t *s, const char *t, size_t n) { if (!sir_loc(s, n)) return; memcpy(s->d + s->n, t, n); s->n += n; s->d[s->n] = 0; } static void sir_text(sir_t *s, const char *t) { if (t) sir_adauga(s, t, strlen(t)); } static void sir_numar(sir_t *s, double v) { char t[64]; /* doua zecimale peste tot: banii se scriu in bani, nu in virgula mobila cu 17 cifre */ sprintf(t, "%.2f", v); sir_text(s, t); } static void sir_cant(sir_t *s, double v) { char t[64]; /* cantitatea are trei zecimale: se vinde si 0,125 kg */ sprintf(t, "%.3f", v); sir_text(s, t); } static void sir_elibereaza(sir_t *s) { free(s->d); s->d = NULL; s->n = s->cap = 0; } /* text scris ca sir JSON, cu ghilimele cu tot */ static void sir_json_text(sir_t *s, const char *t) { sir_text(s, "\""); for (; t && *t; t++) { unsigned char c = (unsigned char) *t; switch (c) { case '"': sir_text(s, "\\\""); break; case '\\': sir_text(s, "\\\\"); break; case '\n': sir_text(s, "\\n"); break; case '\r': sir_text(s, "\\r"); break; case '\t': sir_text(s, "\\t"); break; default: if (c < 0x20) { char u[8]; sprintf(u, "\\u%04x", c); sir_text(s, u); } else { /* octetii >= 0x80 trec asa cum sunt: trupul cererii este UTF-8 */ sir_adauga(s, (const char *) &c, 1); } } } sir_text(s, "\""); } /* == cititorul de JSON ========================================================================= NU este un parser complet. Scoate un camp dupa nume, sarind corect peste siruri, paranteze si escapari. Cauta intai la nivelul curent, apoi in adancime. Pentru liste, ia `r->corp` si da-l unui parser adevarat.*/ static const char *sari_spatii(const char *s) { while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; return s; } /* s este pe ghilimeaua de deschidere; intoarce pointerul de DUPA ghilimeaua de inchidere */ static const char *sari_sir(const char *s) { if (*s != '"') return s; s++; while (*s) { if (*s == '\\' && s[1]) { s += 2; continue; } if (*s == '"') return s + 1; s++; } return s; } static const char *sari_valoare(const char *s) { int adanc = 0; s = sari_spatii(s); if (*s == '"') return sari_sir(s); if (*s != '{' && *s != '[') { while (*s && *s != ',' && *s != '}' && *s != ']') s++; return s; } for (; *s; s++) { if (*s == '"') { s = sari_sir(s) - 1; continue; } if (*s == '{' || *s == '[') adanc++; else if (*s == '}' || *s == ']') { adanc--; if (adanc == 0) return s + 1; } } return s; } /* k este pe ghilimeaua de deschidere a cheii */ static int cheia_e(const char *k, const char *nume) { size_t i; if (*k != '"') return 0; k++; for (i = 0; nume[i]; i++) { if (k[i] != nume[i]) return 0; } return k[i] == '"'; } static const char *gaseste(const char *s, const char *nume); /* o singura trecere printr-un obiect: `direct` = 1 cauta doar la nivelul asta, 0 doar in adancime */ static const char *prin_obiect(const char *s, const char *nume, int direct) { s = sari_spatii(s) + 1; /* peste '{' */ for (;;) { const char *k, *v; s = sari_spatii(s); if (*s != '"') return NULL; /* '}' sau text stricat */ k = s; s = sari_spatii(sari_sir(s)); if (*s != ':') return NULL; v = sari_spatii(s + 1); if (direct) { if (cheia_e(k, nume)) return v; } else { const char *g = gaseste(v, nume); if (g) return g; } s = sari_spatii(sari_valoare(v)); if (*s == ',') { s++; continue; } return NULL; } } static const char *gaseste(const char *s, const char *nume) { s = sari_spatii(s); if (*s == '{') { const char *g = prin_obiect(s, nume, 1); /* intai la nivelul asta */ if (g) return g; return prin_obiect(s, nume, 0); /* apoi in adancime */ } if (*s == '[') { s++; for (;;) { const char *g; s = sari_spatii(s); if (*s == ']' || *s == 0) return NULL; g = gaseste(s, nume); if (g) return g; s = sari_spatii(sari_valoare(s)); if (*s == ',') { s++; continue; } return NULL; } } return NULL; } /* scrie un punct de cod ca UTF-8 */ static size_t utf8(unsigned long c, char *afara) { if (c < 0x80) { afara[0] = (char) c; return 1; } if (c < 0x800) { afara[0] = (char) (0xC0 | (c >> 6)); afara[1] = (char) (0x80 | (c & 0x3F)); return 2; } afara[0] = (char) (0xE0 | (c >> 12)); afara[1] = (char) (0x80 | ((c >> 6) & 0x3F)); afara[2] = (char) (0x80 | (c & 0x3F)); return 3; } /* v este pe ghilimeaua de deschidere a valorii */ static void desface_sir(const char *v, char *afara, size_t marime) { size_t i = 0; if (marime == 0) return; if (*v != '"') { afara[0] = 0; return; } v++; while (*v && *v != '"' && i + 4 < marime) { if (*v == '\\' && v[1]) { v++; switch (*v) { case 'n': afara[i++] = '\n'; break; case 'r': afara[i++] = '\r'; break; case 't': afara[i++] = '\t'; break; case 'b': afara[i++] = '\b'; break; case 'f': afara[i++] = '\f'; break; case 'u': { char h[5]; unsigned long c; memcpy(h, v + 1, 4); h[4] = 0; c = strtoul(h, NULL, 16); i += utf8(c, afara + i); v += 4; break; } default: afara[i++] = *v; } v++; continue; } afara[i++] = *v++; } afara[i] = 0; } int poleo_text(const poleo_raspuns_t *r, const char *nume, char *afara, size_t marime) { const char *v; if (marime) afara[0] = 0; if (!r || !r->corp) return 0; v = gaseste(r->corp, nume); if (!v || *v != '"') return 0; desface_sir(v, afara, marime); return 1; } int poleo_numar(const poleo_raspuns_t *r, const char *nume, double *afara) { const char *v; if (!r || !r->corp) return 0; v = gaseste(r->corp, nume); if (!v) return 0; if (*v == '"') v++; /* unele campuri vin ca text: "12.50" */ if (*v != '-' && *v != '+' && *v != '.' && (*v < '0' || *v > '9')) return 0; *afara = strtod(v, NULL); return 1; } int poleo_adevarat(const poleo_raspuns_t *r, const char *nume, int *afara) { const char *v; if (!r || !r->corp) return 0; v = gaseste(r->corp, nume); if (!v) return 0; if (strncmp(v, "true", 4) == 0) { *afara = 1; return 1; } if (strncmp(v, "false", 5) == 0) { *afara = 0; return 1; } return 0; } /* == statia ==================================================================================== */ static void copiaza(char *unde, size_t marime, const char *ce) { size_t n; if (!ce) { unde[0] = 0; return; } n = strlen(ce); if (n >= marime) n = marime - 1; memcpy(unde, ce, n); unde[n] = 0; } void poleo_init(poleo_t *p, const char *baza, const char *token, const char *casa) { size_t n; memset(p, 0, sizeof(*p)); copiaza(p->baza, sizeof(p->baza), baza ? baza : "http://127.0.0.1:9111"); copiaza(p->token, sizeof(p->token), token ? token : ""); copiaza(p->casa, sizeof(p->casa), casa ? casa : ""); n = strlen(p->baza); while (n && p->baza[n - 1] == '/') p->baza[--n] = 0; } void poleo_din_mediu(poleo_t *p) { poleo_init(p, getenv("POLEO_URL"), getenv("POLEO_TOKEN"), getenv("POLEO_CASA")); if (!p->token[0]) copiaza(p->token, sizeof(p->token), "PUNE-TOKENUL-AICI"); } void poleo_elibereaza(poleo_raspuns_t *r) { if (!r) return; free(r->corp); r->corp = NULL; r->corp_lung = 0; } int poleo_nesuportat(const poleo_raspuns_t *r) { return r && (r->cod == 501 || strcmp(r->cod_eroare, "unsupported") == 0); } /* == transportul =============================================================================== */ #ifdef POLEO_WINHTTP static wchar_t *lat(const char *t) { int n = MultiByteToWideChar(CP_UTF8, 0, t, -1, NULL, 0); wchar_t *w = (wchar_t *) malloc((size_t) n * sizeof(wchar_t)); if (w) MultiByteToWideChar(CP_UTF8, 0, t, -1, w, n); return w; } /* Imparte "http://gazda:port" in bucatile ei. Adresa statiei este o ORIGINE, nu un URL cu cale: calea o pune clientul ("/api/v1/..."). Daca totusi vine ceva in coada, o taiem aici, ca sa nu ajunga in numele gazdei si sa cada rezolvarea cu o eroare care nu spune de ce. */ static int desparte(const char *baza, char *gazda, size_t gn, int *port, int *sigur) { const char *s = baza, *doua, *slash; size_t n; *sigur = 0; *port = 80; if (strncmp(s, "https://", 8) == 0) { s += 8; *sigur = 1; *port = 443; } else if (strncmp(s, "http://", 7) == 0) { s += 7; } slash = strchr(s, '/'); n = slash ? (size_t) (slash - s) : strlen(s); doua = (const char *) memchr(s, ':', n); if (doua) { *port = atoi(doua + 1); n = (size_t) (doua - s); } if (n == 0 || n >= gn) return 0; memcpy(gazda, s, n); gazda[n] = 0; return 1; } static int trimite(poleo_t *p, const char *metoda, const char *cale_intreaga, const char *antete, const char *corp, int timeout, int *status, sir_t *raspuns, char *eroare, size_t en) { char gazda[128]; int port, sigur, gata = 0; wchar_t *wmetoda = NULL, *wcale = NULL, *wgazda = NULL, *wantete = NULL; HINTERNET ses = NULL, con = NULL, cer = NULL; DWORD ms = (DWORD) timeout * 1000; if (!desparte(p->baza, gazda, sizeof(gazda), &port, &sigur)) { copiaza(eroare, en, "adresa statiei nu se poate citi"); return 0; } wmetoda = lat(metoda); wcale = lat(cale_intreaga); wgazda = lat(gazda); wantete = lat(antete); if (!wmetoda || !wcale || !wgazda || !wantete) { copiaza(eroare, en, "memorie insuficienta"); goto iesire; } ses = WinHttpOpen(L"PoleoFiscal-C/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!ses) { copiaza(eroare, en, "nu pot porni sesiunea HTTP"); goto iesire; } WinHttpSetTimeouts(ses, 5000, 5000, (int) ms, (int) ms); con = WinHttpConnect(ses, wgazda, (INTERNET_PORT) port, 0); if (!con) { copiaza(eroare, en, "nu ma pot conecta"); goto iesire; } cer = WinHttpOpenRequest(con, wmetoda, wcale, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, sigur ? WINHTTP_FLAG_SECURE : 0); if (!cer) { copiaza(eroare, en, "nu pot deschide cererea"); goto iesire; } WinHttpAddRequestHeaders(cer, wantete, (DWORD) -1, WINHTTP_ADDREQ_FLAG_ADD); if (!WinHttpSendRequest(cer, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID) corp, corp ? (DWORD) strlen(corp) : 0, corp ? (DWORD) strlen(corp) : 0, 0) || !WinHttpReceiveResponse(cer, NULL)) { DWORD e = GetLastError(); char t[160]; sprintf(t, "statia nu raspunde (cod Windows %lu)", (unsigned long) e); copiaza(eroare, en, t); goto iesire; } { DWORD cod = 0, n = sizeof(cod); WinHttpQueryHeaders(cer, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &cod, &n, WINHTTP_NO_HEADER_INDEX); *status = (int) cod; } for (;;) { DWORD disponibil = 0, citit = 0; char bucata[8192]; if (!WinHttpQueryDataAvailable(cer, &disponibil) || disponibil == 0) break; while (disponibil > 0) { DWORD cere = disponibil > sizeof(bucata) ? (DWORD) sizeof(bucata) : disponibil; if (!WinHttpReadData(cer, bucata, cere, &citit) || citit == 0) { disponibil = 0; break; } sir_adauga(raspuns, bucata, citit); disponibil -= citit; } } gata = 1; iesire: if (cer) WinHttpCloseHandle(cer); if (con) WinHttpCloseHandle(con); if (ses) WinHttpCloseHandle(ses); free(wmetoda); free(wcale); free(wgazda); free(wantete); return gata; } #else /* POLEO_CURL */ static size_t aduna(void *date, size_t m, size_t n, void *ctx) { sir_adauga((sir_t *) ctx, (const char *) date, m * n); return m * n; } static int trimite(poleo_t *p, const char *metoda, const char *cale_intreaga, const char *antete, const char *corp, int timeout, int *status, sir_t *raspuns, char *eroare, size_t en) { CURL *c = curl_easy_init(); struct curl_slist *lista = NULL; char url[1024], *linie, *urm; char *copie; CURLcode cod; long st = 0; if (!c) { copiaza(eroare, en, "nu pot porni curl"); return 0; } snprintf(url, sizeof(url), "%s%s", p->baza, cale_intreaga); /* antetele vin ca un singur bloc separat prin \r\n, ca la WinHTTP */ copie = strdup(antete); for (linie = copie; linie && *linie; linie = urm) { urm = strstr(linie, "\r\n"); if (urm) { *urm = 0; urm += 2; } else urm = linie + strlen(linie); if (*linie) lista = curl_slist_append(lista, linie); } curl_easy_setopt(c, CURLOPT_URL, url); curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, metoda); curl_easy_setopt(c, CURLOPT_HTTPHEADER, lista); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, aduna); curl_easy_setopt(c, CURLOPT_WRITEDATA, raspuns); curl_easy_setopt(c, CURLOPT_TIMEOUT, (long) timeout); curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, 5L); if (corp) { curl_easy_setopt(c, CURLOPT_POSTFIELDS, corp); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long) strlen(corp)); } cod = curl_easy_perform(c); curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &st); *status = (int) st; curl_slist_free_all(lista); free(copie); curl_easy_cleanup(c); if (cod != CURLE_OK) { copiaza(eroare, en, curl_easy_strerror(cod)); return 0; } return 1; } #endif 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) { sir_t antete = {0, 0, 0}, corp_raspuns = {0, 0, 0}; char cale_intreaga[1024], eroare[256]; int status = 0, mers; memset(r, 0, sizeof(*r)); snprintf(cale_intreaga, sizeof(cale_intreaga), "/api/v1/%s", cale[0] == '/' ? cale + 1 : cale); sir_text(&antete, "Authorization: Bearer "); sir_text(&antete, p->token); sir_text(&antete, "\r\nAccept: application/json"); if (corp_json) sir_text(&antete, "\r\nContent-Type: application/json; charset=utf-8"); if (cheie && cheie[0]) { sir_text(&antete, "\r\nIdempotency-Key: "); sir_text(&antete, cheie); } eroare[0] = 0; mers = trimite(p, metoda, cale_intreaga, antete.d ? antete.d : "", corp_json, timeout, &status, &corp_raspuns, eroare, sizeof(eroare)); sir_elibereaza(&antete); if (!mers) { /* cod 0 inseamna ca NU am ajuns la statie - alt caz decat un refuz al ei */ r->ok = 0; r->cod = 0; snprintf(r->mesaj, sizeof(r->mesaj), "nu am ajuns la statie (%s). Ruleaza Poleo Fiscal? E pornit API-ul din " "Setari -> API? Adresa %s este buna?", eroare, p->baza); sir_elibereaza(&corp_raspuns); return 0; } r->cod = status; r->corp = corp_raspuns.d; r->corp_lung = corp_raspuns.n; if (!r->corp) { r->corp = (char *) calloc(1, 1); r->corp_lung = 0; } r->ok = (status >= 200 && status < 300); if (!r->ok) { /* forma normala: {"error": {"code": "...", "message": "..."}} */ if (!poleo_text(r, "message", r->mesaj, sizeof(r->mesaj)) || !r->mesaj[0]) { snprintf(r->mesaj, sizeof(r->mesaj), "statia a refuzat cererea (HTTP %d)", status); } poleo_text(r, "code", r->cod_eroare, sizeof(r->cod_eroare)); } return r->ok; } /* == constructorul de bon ====================================================================== */ struct poleo_bon { sir_t articole; sir_t plati; sir_t gata; }; poleo_bon_t *poleo_bon_nou(void) { poleo_bon_t *b = (poleo_bon_t *) calloc(1, sizeof(poleo_bon_t)); return b; } void poleo_bon_elibereaza(poleo_bon_t *b) { if (!b) return; sir_elibereaza(&b->articole); sir_elibereaza(&b->plati); sir_elibereaza(&b->gata); free(b); } static void virgula(sir_t *s) { if (s->n) sir_text(s, ","); } static void articol(poleo_bon_t *b, const char *nume, const char *cod, double pret, double cant, const char *um, int cota, const char *fel, double val, int storno) { char t[32]; virgula(&b->articole); sir_text(&b->articole, "{"); if (cod) { sir_text(&b->articole, "\"code\":"); sir_json_text(&b->articole, cod); } else { sir_text(&b->articole, "\"name\":"); sir_json_text(&b->articole, nume); sir_text(&b->articole, ",\"price_val\":"); sir_numar(&b->articole, pret); } sir_text(&b->articole, ",\"qty\":"); sir_cant(&b->articole, cant); if (um && um[0]) { sir_text(&b->articole, ",\"unit\":"); sir_json_text(&b->articole, um); } sprintf(t, ",\"vat_rate\":%d", cota); sir_text(&b->articole, t); if (fel && fel[0]) { sir_text(&b->articole, ",\"adjust_kind\":"); sir_json_text(&b->articole, fel); sir_text(&b->articole, ",\"adjust_value\":"); sir_numar(&b->articole, val); } if (storno) sir_text(&b->articole, ",\"storno\":true"); sir_text(&b->articole, "}"); } void poleo_bon_articol(poleo_bon_t *b, const char *nume, double pret, double cant, const char *um, int cota_tva) { articol(b, nume, NULL, pret, cant, um, cota_tva, NULL, 0, 0); } 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(b, nume, NULL, pret, cant, um, cota_tva, fel, valoare, 0); } void poleo_bon_articol_cod(poleo_bon_t *b, const char *cod, double cant, int cota_tva) { articol(b, NULL, cod, 0, cant, NULL, cota_tva, NULL, 0, 0); } void poleo_bon_storno(poleo_bon_t *b, const char *nume, double pret, double cant, int cota_tva) { articol(b, nume, NULL, pret, cant, NULL, cota_tva, NULL, 0, 1); } void poleo_bon_plata(poleo_bon_t *b, const char *metoda, double suma) { virgula(&b->plati); sir_text(&b->plati, "{\"method\":"); sir_json_text(&b->plati, metoda); sir_text(&b->plati, ",\"amount\":"); sir_numar(&b->plati, suma); sir_text(&b->plati, "}"); } static const char *bon_json(poleo_bon_t *b, const char *casa) { b->gata.n = 0; if (b->gata.d) b->gata.d[0] = 0; sir_text(&b->gata, "{\"receipt\":{\"items\":["); sir_text(&b->gata, b->articole.d ? b->articole.d : ""); sir_text(&b->gata, "],\"payments\":["); sir_text(&b->gata, b->plati.d ? b->plati.d : ""); sir_text(&b->gata, "]}"); if (casa && casa[0]) { sir_text(&b->gata, ",\"case\":"); sir_json_text(&b->gata, casa); } sir_text(&b->gata, "}"); return b->gata.d; } const char *poleo_bon_json(poleo_bon_t *b) { return bon_json(b, NULL); } /* == metodele API ============================================================================== `nui()` alege casa: cea data la apel, altfel cea din `poleo_t`, altfel "-" (statia isi alege casa obisnuita). Filtrul `&case=` se pune la listele de evidenta: pe o statie cu mai multe case listele intorc salvarile TUTUROR caselor, iar detaliile uneia straine intorc 404 - nu fiindca este stricat ceva, ci fiindca ai intrebat de casa altcuiva.*/ static const char *nui(poleo_t *p, const char *dat) { if (dat && dat[0]) return dat; if (p->casa[0]) return p->casa; return "-"; } static int lista(poleo_t *p, const char *cale, int limit, poleo_raspuns_t *r) { char c[256]; snprintf(c, sizeof(c), "%s?limit=%d%s%s", cale, limit, p->casa[0] ? "&case=" : "", p->casa[0] ? p->casa : ""); return poleo_cere(p, "GET", c, NULL, NULL, POLEO_TIMEOUT_SCURT, r); } static int simplu(poleo_t *p, const char *cale, poleo_raspuns_t *r) { return poleo_cere(p, "GET", cale, NULL, NULL, POLEO_TIMEOUT_SCURT, r); } int poleo_sanatate(poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "health", r); } int poleo_metode (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "methods", r); } int poleo_casele (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "cases", r); } int poleo_probleme(poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "problems", r); } int poleo_sertar (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "drawer", r); } int poleo_totaluri(poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "totals", r); } int poleo_cote (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "vat", r); } int poleo_anaf (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "anaf", r); } int poleo_licenta (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "license", r); } int poleo_statie (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "station", r); } int poleo_terminale(poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "terminals", r); } int poleo_firma (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "company", r); } int poleo_setari (poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "settings", r); } int poleo_motive_declaratie(poleo_t *p, poleo_raspuns_t *r) { return simplu(p, "declaration-reasons", r); } int poleo_stare(poleo_t *p, const char *n, poleo_raspuns_t *r) { char c[160]; snprintf(c, sizeof(c), "cases/%s/status", nui(p, n)); return poleo_cere(p, "GET", c, NULL, NULL, POLEO_TIMEOUT_LUNG, r); } int poleo_config(poleo_t *p, const char *n, poleo_raspuns_t *r) { char c[160]; snprintf(c, sizeof(c), "cases/%s/config", nui(p, n)); return poleo_cere(p, "GET", c, NULL, NULL, POLEO_TIMEOUT_LUNG, r); } int poleo_tipareste(poleo_t *p, poleo_bon_t *b, const char *cheie, poleo_raspuns_t *r) { return poleo_cere(p, "POST", "receipts", bon_json(b, p->casa), cheie, POLEO_TIMEOUT_LUNG, r); } int poleo_bonuri (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "receipts", l, r); } int poleo_plati_card (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "card-payments", l, r); } int poleo_rapoarte (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "reports", l, r); } int poleo_miscari_numerar(poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "cash", l, r); } int poleo_je (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "je", l, r); } int poleo_retururi (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "returns", l, r); } int poleo_declaratii (poleo_t *p, int l, poleo_raspuns_t *r) { return lista(p, "declarations", l, r); } int poleo_posta(poleo_t *p, int l, poleo_raspuns_t *r) { char c[64]; snprintf(c, sizeof(c), "emails?limit=%d", l); return simplu(p, c, r); } int poleo_bonul(poleo_t *p, const char *id, poleo_raspuns_t *r) { char c[160]; snprintf(c, sizeof(c), "receipts/%s", id); return simplu(p, c, r); } int poleo_je_salvare(poleo_t *p, const char *id, poleo_raspuns_t *r) { char c[160]; snprintf(c, sizeof(c), "je/%s", id); return simplu(p, c, r); } int poleo_je_fisier(poleo_t *p, const char *id, const char *nume, poleo_raspuns_t *r) { char c[320]; snprintf(c, sizeof(c), "je/%s/files/%s", id, nume); return poleo_cere(p, "GET", c, NULL, NULL, POLEO_TIMEOUT_LUNG, r); } int poleo_declaratie_fisier(poleo_t *p, const char *id, poleo_raspuns_t *r) { char c[160]; snprintf(c, sizeof(c), "declarations/%s/file", id); return poleo_cere(p, "GET", c, NULL, NULL, POLEO_TIMEOUT_LUNG, r); } int poleo_copie(poleo_t *p, const char *cheie, poleo_raspuns_t *r) { return poleo_cere(p, "POST", "copy", "{}", cheie, POLEO_TIMEOUT_LUNG, r); } int poleo_anuleaza_bon_deschis(poleo_t *p, const char *cheie, poleo_raspuns_t *r) { return poleo_cere(p, "POST", "void", "{}", cheie, POLEO_TIMEOUT_LUNG, r); } int poleo_deschide_sertar(poleo_t *p, const char *cheie, poleo_raspuns_t *r) { return poleo_cere(p, "POST", "drawer", "{}", cheie, POLEO_TIMEOUT_LUNG, r); } int poleo_raport(poleo_t *p, const char *fel, const char *optiuni_json, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"kind\":"); sir_json_text(&c, fel); if (optiuni_json && optiuni_json[0]) { sir_text(&c, ","); sir_text(&c, optiuni_json); } sir_text(&c, "}"); ok = poleo_cere(p, "POST", "reports", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_numerar(poleo_t *p, const char *fel, double suma, const char *motiv, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"kind\":"); sir_json_text(&c, fel); sir_text(&c, ",\"amount\":"); sir_numar(&c, suma); sir_text(&c, ",\"reason\":"); sir_json_text(&c, motiv); sir_text(&c, "}"); ok = poleo_cere(p, "POST", "cash", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_export_je(poleo_t *p, const char *de_la, const char *pana_la, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"from\":"); sir_json_text(&c, de_la); sir_text(&c, ",\"to\":"); sir_json_text(&c, pana_la); sir_text(&c, "}"); ok = poleo_cere(p, "POST", "anaf", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } static int pos_suma(poleo_t *p, const char *cale, double suma, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"amount\":"); sir_numar(&c, suma); sir_text(&c, "}"); ok = poleo_cere(p, "POST", cale, c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_pos (poleo_t *p, double s, const char *k, poleo_raspuns_t *r) { return pos_suma(p, "pos", s, k, r); } int poleo_pos_retur(poleo_t *p, double s, const char *k, poleo_raspuns_t *r) { return pos_suma(p, "pos/refund", s, k, r); } int poleo_pos_anulare(poleo_t *p, const char *stan, double suma, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"stan\":"); sir_json_text(&c, stan); sir_text(&c, ",\"amount\":"); sir_numar(&c, suma); sir_text(&c, "}"); ok = poleo_cere(p, "POST", "pos/void", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_pos_inchidere_zi(poleo_t *p, const char *cheie, poleo_raspuns_t *r) { return poleo_cere(p, "POST", "pos/settlement", "{}", cheie, POLEO_TIMEOUT_LUNG, r); } int poleo_retur(poleo_t *p, const char *bon_id, const char *motiv, const char *cerut_de, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"receipt\":"); sir_json_text(&c, bon_id); sir_text(&c, ",\"reason\":"); sir_json_text(&c, motiv); sir_text(&c, ",\"requested_by\":"); sir_json_text(&c, cerut_de); /* `acknowledged` inseamna ca OMUL a decis returul. Il trimitem true fiindca aici il ceri explicit din cod; nu-l lega de un camp ascuns intr-un formular. */ sir_text(&c, ",\"acknowledged\":true}"); ok = poleo_cere(p, "POST", "returns", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_declaratie(poleo_t *p, const char *bon_id, const char *motiv_cheie, const char *operator_, const char *cheie, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"receipt\":"); sir_json_text(&c, bon_id); sir_text(&c, ",\"reasons\":["); sir_json_text(&c, motiv_cheie); sir_text(&c, "],\"operator\":"); sir_json_text(&c, operator_); sir_text(&c, ",\"cash_out\":false,\"acknowledged\":true}"); ok = poleo_cere(p, "POST", "declarations", c.d, cheie, POLEO_TIMEOUT_LUNG, r); sir_elibereaza(&c); return ok; } int poleo_scrie_setari(poleo_t *p, const char *setari_json, poleo_raspuns_t *r) { sir_t c = {0, 0, 0}; int ok; sir_text(&c, "{\"settings\":"); sir_text(&c, setari_json); sir_text(&c, "}"); ok = poleo_cere(p, "POST", "settings", c.d, NULL, POLEO_TIMEOUT_SCURT, r); sir_elibereaza(&c); return ok; }