> Source: https://fiscal.link/integrators/docs/reference/local/receipts
> Bonul fiscal și bonul nefiscal, dintr-un singur apel.

API local

# Bonuri

Bonul fiscal și bonul nefiscal, dintr-un singur apel.

## Resursa PrintFiscalReceiptRequest

`openFiscalReceipt` [object](https://fiscal.link/integrators/docs/reference/objects/local-open-fiscal-receipt-request) poate fi null

Antetul bonului. Dacă lipsește: operatorul 1, parola 0001, punctul de vânzare 1, fără CIF client.

`receiptItems` [array](https://fiscal.link/integrators/docs/reference/objects/local-receipt-item) obligatoriu

Liniile bonului, tipărite în ordine.

Lipsă: 400 `RECEIPT_ITEMS_MISSING`. Listă goală: 400 `RECEIPT_ITEMS_EMPTY`.

`subtotal` [object](https://fiscal.link/integrators/docs/reference/objects/local-subtotal-request) poate fi null

Pasul de subtotal. Se face mereu; aici alegi dacă se tipărește, se afișează și dacă are adaos sau reducere.

`payments` [array](https://fiscal.link/integrators/docs/reference/objects/local-payment-request) poate fi null

Plățile bonului. Dacă lipsesc, tot bonul se plătește în numerar; o listă goală nu trimite nicio plată.

### Obiecte folosite aici

-   [PrintTextRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-text-request)
-   [OpenFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-open-fiscal-receipt-request)
-   [RegisterFreeSaleRequest](https://fiscal.link/integrators/docs/reference/objects/local-free-sale-request)
-   [RegisterPluSaleRequest](https://fiscal.link/integrators/docs/reference/objects/local-plu-sale-request)
-   [SubtotalRequest](https://fiscal.link/integrators/docs/reference/objects/local-subtotal-request)
-   [RegisterPaymentRequest](https://fiscal.link/integrators/docs/reference/objects/local-payment-request)
-   [BarCodeRequest](https://fiscal.link/integrators/docs/reference/objects/local-bar-code-request)
-   [ReceiptItemRequest](https://fiscal.link/integrators/docs/reference/objects/local-receipt-item)
-   [IdempotencyHeaders](https://fiscal.link/integrators/docs/reference/objects/local-idempotency-headers)
-   [ApiResponse](https://fiscal.link/integrators/docs/reference/objects/local-response-envelope)
-   [ErrorResponse](https://fiscal.link/integrators/docs/reference/objects/local-error-response)
-   [OpenNonFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-open-non-fiscal-receipt-request)
-   [PrintNonFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-non-fiscal-receipt-request)
-   [NonFiscalReceiptItemRequest](https://fiscal.link/integrators/docs/reference/objects/local-non-fiscal-receipt-item)

## Rute

POST `/print-fiscal-receipt`

### Tipărește un bon fiscal

Primește tot bonul și face singur deschiderea, liniile, subtotalul, plățile și închiderea, într-un singur apel.

Fără `openFiscalReceipt`, bonul se deschide cu operatorul 1, parola `0001`, punctul de vânzare 1, fără factură. Fără `payments`, tot totalul se achită automat în numerar.

#### Câmpuri

| Câmp | Tip | Obligatoriu | Descriere |
| --- | --- | --- | --- |
| `openFiscalReceipt` | object | Nu | Antetul bonului: operator, parolă, punct de vânzare, factură. Dacă îl trimiți, `pointOfSaleNumber` devine obligatoriu și, pe Datecs și Daisy, la fel `operatorCode` și `operatorPassword`. |
| `openFiscalReceipt.operatorCode` | int | Condiționat | Operatorul, 1–30. Pe Tremol, dacă lipsește, aplicația îl citește de pe casă (operatorul 1). |
| `openFiscalReceipt.operatorPassword` | string | Condiționat | Parola operatorului, 1–8 cifre. O parolă greșită e un refuz al casei (`400`). Pe Tremol se completează cu `0` sau se taie la exact 4 caractere. |
| `openFiscalReceipt.pointOfSaleNumber` | int | Condiționat | Punctul de vânzare, 1–99999 (pe Datecs cel mult 10000). |
| `openFiscalReceipt.invoice` | bool | Nu | `true` deschide pe Datecs un bon „CIF Client”. Daisy și Tremol îl ignoră. |
| `receiptItems` | array | Da | Liniile bonului, tipărite în ordine. |
| `receiptItems[].type` | int | Da | `0` vânzare liberă (`freeSale`), `1` vânzare de articol PLU (`pluSale`), `2` text (`text`), `3` cod de bare (`barCode`), `4` linie de separare (`separatingLineType`). Textul, codul de bare și linia nu intră în total. |
| `receiptItems[].freeSale` | object | Condiționat: `type` = 0 | Un produs descris complet în cerere. Modelul `freeSale` e mai jos. |
| `receiptItems[].pluSale` | object | Condiționat: `type` = 1 | Un articol programat în casă; casa îi dă numele, prețul și grupa TVA. Modelul `pluSale` e mai jos. |
| `receiptItems[].text` | object | Condiționat: `type` = 2 | O linie de text. Modelul `text` e mai jos. |
| `receiptItems[].barCode` | object | Condiționat: `type` = 3 | Un cod de bare. Modelul `barCode` e la „Coduri de bare și linii”. |
| `receiptItems[].separatingLineType` | int | Condiționat: `type` = 4 | Stilul liniei, 1–4 (1–3 pe Datecs). |
| `subtotal` | object | Nu | Pasul de subtotal: tipărire, afișare și o reducere sau majorare pe tot bonul. Modelul `subtotal` e mai jos. |
| `payments` | array | Nu | Plățile, aplicate în ordine după subtotal. Câmpurile fiecărei plăți sunt mai jos. |

#### Modele

| Model · câmp | Tip | Obligatoriu | Descriere |
| --- | --- | --- | --- |
| `freeSale.productName` | string | Da | Numele tipărit. Diacriticele devin litere latine. Maximum 72 de caractere pe Datecs și Daisy, peste e refuzat; pe Tremol 138, din care 30 pe linia de vânzare. |
| `freeSale.taxCode` | int | Da | Grupa TVA: `1` = A, `2` = B și așa mai departe. Cota fiecărei grupe e programată în casă. Datecs acceptă 1–7; un Datecs al unei firme neplătitoare de TVA vinde doar pe 5 și 6. Tremol acceptă 1–6 (A–E și F, „Alte taxe”); orice altă valoare, inclusiv 7 și 8, e refuzată cu `400` înainte să se deschidă bonul. |
| `freeSale.price` | decimal | Nu | Prețul unitar, cu TVA, de la 0, cel mult 2 zecimale. Maximum 9.999.999,99 pe Datecs și 999.999,99 pe Tremol; pe Daisy cel mult 8 cifre. Nu folosi preț negativ: anulezi o linie cu `void`. |
| `freeSale.quantity` | decimal | Nu | Cantitatea, implicit 1, de la 0,01, cel mult 3 zecimale. Maximum 99.999,999 pe Datecs și Tremol; pe Daisy 99.999.999,999. |
| `freeSale.department` | int | Nu | Departamentul; lipsă sau `0` înseamnă fără departament. 0–99 pe Datecs și Daisy; 0–19 pe Tremol, iar 19 cu grupa F e refuzat. |
| `freeSale.unit` | string | Da | Unitatea de măsură, 1–6 caractere (de exemplu `buc`); pe Tremol cel mult 3. |
| `freeSale.void` | bool | Nu | `true` înregistrează linia ca anulată: se scade din bon. |
| `freeSale.discountType` | int | Nu | Reducere sau majorare pe linie: `0` niciuna, `1` majorare %, `2` reducere %, `3` majorare sumă, `4` reducere sumă. Tremol nu trimite reducerile pe linie. |
| `freeSale.discountValue` | decimal | Condiționat: `discountType` = 1–4 | Procent 0,01–100 (tipurile 1 și 2) sau sumă 0,01–9.999.999,99 (tipurile 3 și 4), cel mult 2 zecimale. |
| `pluSale.pluNumber` | int | Da | Numărul articolului în casă, mai mare ca 0. Un articol neprogramat e un refuz al casei (`400`, bonul se anulează). |
| `pluSale.quantity` | decimal | Nu | Cantitatea, implicit 1, de la 0,01, cel mult 3 zecimale. Maximum 99.999,999 pe Datecs și Tremol; pe Daisy 999.999,999. |
| `pluSale.void`, `discountType`, `discountValue` | — | Nu | Ca la `freeSale`. |
| `text.message` | string | Da | Textul de tipărit. Gol: `400`. Pe Tremol se taie la 33 de caractere. |
| `text.bold`, `italic`, `underline` | bool | Nu | Îngroșat, cursiv, subliniat. Doar Datecs. |
| `text.height` | int | Nu | `0` normală, `1` dublă, `2` pe jumătate (refuzată pe Datecs). Are efect doar pe Datecs. |
| `text.alignment` | int | Nu | `0` stânga, `1` centru, `2` dreapta. Doar Datecs. |
| `subtotal.print` / `display` | bool | Nu | Tipărește subtotalul pe bon / îl arată pe afișajul clientului. Pasul de subtotal rulează oricum. |
| `subtotal.discountType` / `discountValue` | int / decimal | Nu | Reducere sau majorare pe tot bonul, cu aceleași valori ca pe linie. |
| `payments[].paymentMode` | int | Da | Codul de plată al casei, 0–99. Codurile diferă între mărci: vezi [„Diferențe între mărci”](#diferente-intre-marci). |
| `payments[].amount` | decimal | Da | Suma, 0,01–9.999.999,99 (pe Tremol până la 999.999,99), cel mult 2 zecimale. Doar numerarul poate depăși ce mai e de plată (casa dă rest); orice altă metodă se oprește la suma rămasă. |

#### Reguli

-   Aplicația verifică bonul înainte să-l deschidă. Un bon care eșuează după deschidere e anulat, iar mesajul se termină cu `| Receipt cancelled successfully`. Niciun bon nu rămâne deschis.
-   Cu verificarea cotelor TVA pornită în aplicație, o grupă care nu poartă o cotă încă în vigoare (21%, 11% sau 0%) e refuzată cu `400`, înainte de deschidere.
-   Plățile care nu acoperă totalul sunt un refuz al casei, iar bonul se anulează. Pe Datecs, o plată automată la un total de 0 sau mai mic e refuzată și bonul se anulează.
-   Daisy nu vinde linii libere direct. Cu „Vânzare liberă pe Daisy” pornită în aplicație (sau de organizația de integrator care a activat casa), fiecare linie liberă se vinde ca articol programat: aplicația caută articolul după nume, preț și departamentul grupei TVA și, dacă lipsește, îl programează întâi. Articolele folosite vin înapoi în rezultatul bonului. Cu setarea oprită, bonul cu linii libere e refuzat cu `400`, înainte de deschidere.
-   Pe API-ul local, `data` e mesajul de succes, în limba aplicației. Numărul bonului, totalurile și articolele folosite vin doar prin API-ul Cloud, în rezultatul comenzii.

#### Cerere completă

```json
{
  "openFiscalReceipt": {
    "operatorCode": 1,
    "operatorPassword": "0001",
    "pointOfSaleNumber": 1,
    "invoice": false
  },
  "receiptItems": [
    { "type": 2, "text": { "message": "Ref. 128", "alignment": 1 } },
    { "type": 0, "freeSale": { "productName": "Espresso", "taxCode": 1, "price": 8.50, "quantity": 2, "department": 1, "unit": "buc" } },
    { "type": 1, "pluSale": { "pluNumber": 101, "quantity": 1 } },
    { "type": 4, "separatingLineType": 2 }
  ],
  "subtotal": { "print": true },
  "payments": [
    { "paymentMode": 0, "amount": 50.00 }
  ]
}
```

`paymentMode` `0` e numerar pe Datecs și Tremol; pe Daisy numerarul are codul `3`.

#### Coduri de răspuns

| Status | Descriere |
| --- | --- |
| 200 | Bonul s-a tipărit. |
| 400 | Cererea încalcă o regulă (interval, lungime, obiect lipsă) sau casa a refuzat un pas. Un bon deschis e anulat. |
| 401 | Casa conectată nu are licență. |
| 424 | Nicio casă conectată, sau legătura cu casa a căzut. |
| 500 | Casa n-a răspuns la timp, sau o eroare neașteptată în aplicație. |

-   Cerere [PrintTextRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-text-request)
-   Cerere [OpenFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-open-fiscal-receipt-request)
-   Cerere [RegisterFreeSaleRequest](https://fiscal.link/integrators/docs/reference/objects/local-free-sale-request)
-   Cerere [RegisterPluSaleRequest](https://fiscal.link/integrators/docs/reference/objects/local-plu-sale-request)
-   Cerere [SubtotalRequest](https://fiscal.link/integrators/docs/reference/objects/local-subtotal-request)
-   Cerere [RegisterPaymentRequest](https://fiscal.link/integrators/docs/reference/objects/local-payment-request)
-   Cerere [BarCodeRequest](https://fiscal.link/integrators/docs/reference/objects/local-bar-code-request)
-   Cerere [PrintFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-fiscal-receipt-request)
-   Cerere [ReceiptItemRequest](https://fiscal.link/integrators/docs/reference/objects/local-receipt-item)
-   Cerere [IdempotencyHeaders](https://fiscal.link/integrators/docs/reference/objects/local-idempotency-headers)
-   Răspuns [ApiResponse](https://fiscal.link/integrators/docs/reference/objects/local-response-envelope)
-   Răspuns [ErrorResponse](https://fiscal.link/integrators/docs/reference/objects/local-error-response)

POSTCerere

```bash
curl -X POST 'https://localhost.fiscallink.ro:65401/api/v2/print-fiscal-receipt' \
  -H 'Content-Type: application/json' \
  -d '{
  "receiptItems": [
    {
      "type": 0,
      "freeSale": {
        "productName": "Espresso",
        "taxCode": 1,
        "price": 8.5,
        "quantity": 2,
        "department": 1,
        "unit": "buc"
      }
    }
  ]
}'
```

200Răspuns

```json
{
  "statusCode": 200,
  "data": "Receipt printed successfully."
}
```

400Răspuns

```json
{
  "statusCode": 400,
  "message": "Receipt printing error: <text> (<code>) | Receipt cancelled successfully"
}
```

POST `/print-non-fiscal-receipt`

### Tipărește un bon nefiscal

Tipărește tot bonul nefiscal dintr-un singur apel: linii de text, coduri de bare și linii de separare. Fără vânzări, fără plăți, fără verificare TVA.

| Câmp | Tip | Obligatoriu | Descriere |
| --- | --- | --- | --- |
| `receiptItems` | array | Da | Liniile, tipărite în ordine. |
| `receiptItems[].type` | int | Da | `0` text (`text`), `1` cod de bare (`barCode`), `2` linie de separare (`separatingLineType`). Numerele diferă de cele de la bonul fiscal. |

-   Cerere [OpenNonFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-open-non-fiscal-receipt-request)
-   Cerere [PrintTextRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-text-request)
-   Cerere [BarCodeRequest](https://fiscal.link/integrators/docs/reference/objects/local-bar-code-request)
-   Cerere [PrintNonFiscalReceiptRequest](https://fiscal.link/integrators/docs/reference/objects/local-print-non-fiscal-receipt-request)
-   Cerere [NonFiscalReceiptItemRequest](https://fiscal.link/integrators/docs/reference/objects/local-non-fiscal-receipt-item)
-   Răspuns [ApiResponse](https://fiscal.link/integrators/docs/reference/objects/local-response-envelope)
-   Răspuns [ErrorResponse](https://fiscal.link/integrators/docs/reference/objects/local-error-response)

POSTCerere

```bash
curl -X POST 'https://localhost.fiscallink.ro:65401/api/v2/print-non-fiscal-receipt' \
  -H 'Content-Type: application/json' \
  -d '{
  "receiptItems": [
    {
      "type": 0,
      "text": {
        "message": "Ref. 128",
        "bold": true,
        "alignment": 1
      }
    },
    {
      "type": 2,
      "separatingLineType": 1
    },
    {
      "type": 1,
      "barCode": {
        "type": 2,
        "data": "5940000000820"
      }
    }
  ]
}'
```

200Răspuns

```json
{
  "statusCode": 200,
  "data": "Non-fiscal receipt printed successfully."
}
```
