# Ordini

> Crea ordini clienti, registra l'adempimento e le correzioni, e aggiungi tracciamento della consegna opzionale.

Gli acquirenti effettuano gli ordini nel loro portale commerciale. I fornitori gestiscono le fatture. Possono offrire opzionalmente [pagamenti clienti ospitati da Stripe](/docs/guides/customer-payments) dopo aver confermato il totale finale. Le richieste di ordine e il credito commerciale rimangono disponibili. Utilizza l'API per estrarre gli ordini nel tuo ERP o sistema contabile e per aggiornare lo stato dei loro ordini.

## Stati degli ordini

| Stato          | Significato                                 |
| -------------- | ------------------------------------------- |
| `new`          | Piazzato                                    |
| `pending`      | In attesa                                   |
| `acknowledged` | Visualizzato e in fase di prelievo          |
| `processed`    | Pronto per la spedizione                    |
| `dispatched`   | Inviato dal fornitore                       |
| `completed`    | Fornitore ha completato                     |
| `cancelled`    | Annullato con un motivo visibile al cliente |

Il personale può saltare le fasi intermedie di prelievo. Spostarsi all'indietro o riaprire un ordine completato o annullato richiede un `correctionReason` interno. La cronologia precedente rimane. Le correzioni non modificano mai lo stato dei pagamenti. Leggi `revision` e invia `expectedRevision` con gli aggiornamenti per rifiutare le modifiche obsolete.

## Crea un ordine per un cliente

**POST** `/api/v1/orders/preview`

Richiede `orders.create` e l'accesso al cliente. Inviare `customerAccountId` e `lines` contenenti `productId` e un intero `qty` (da 1 a 1.000.000). Selezionare ogni prodotto ordinario una volta, al massimo 100 righe. Una riga può includere un `configuration` opzionale contenente le chiavi di misurazione e scelta restituite; le diverse configurazioni di un prodotto rimangono separate. Un `lineKey` opzionale assegna un'identità stabile a ciascuna riga di anteprima. Un ulteriore `additionalDiscountBp` richiede il permesso di prezzo e `discountReason`. Le letture dell'ordine configurato includono uno snapshot `configuration` immutabile con input, specifiche, versione, arrotondamento e costi. La preview restituisce `total`, `fingerprint`, i prezzi correnti del cliente e la valuta. Gli account sospesi, i prodotti non disponibili e le quantità inferiori al minimo vengono rifiutati. Un catalogo con prezzi solo accetta un ordine immesso qui; i compratori non possono inviarne uno da soli mentre è disattivato.

**POST** `/api/v1/orders`

Inviare lo stesso cliente e le stesse righe, `expectedTotal` dalla preview, `expectedBasket` impostato sul suo fingerprint, e un UUID nuovo `requestKey`. Un `poNumber` opzionale (60 caratteri) e un `notes` visibile dal cliente (2.000 caratteri) sono supportati. Riutilizzare esattamente la stessa richiesta e chiave dopo una risposta incerta: un retry riuscito restituisce l'ordine originale invece di crearne un altro. I prezzi modificati richiedono un'altra revisione. Il risultato è `placed` con `orderId` e `reference`, o `prices_changed`, `empty` o `unavailable`.

Il personale agisce sotto la propria identità. Il carrello del cliente rimane intatto. I contatti email del cliente possono ricevere una copia; questo non comporta alcun addebito di pagamento. Il Sales Rep ha accesso alla creazione per default; i aggiornamenti di elaborazione e consegna richiedono il permesso separato `orders.process`.

## Consegna e tracciamento

**PATCH** `/api/v1/orders/{id}/delivery`

Richiede `orders.process`. Inviare `deliveryMethod`: `unspecified`, `courier`, `own_delivery` o `collection`. Un `courierName` opzionale (100 caratteri), un `trackingReference` (120 caratteri) e un `trackingUrl` (1.000 caratteri) sono visibili dal cliente. I link di tracciamento devono utilizzare HTTPS senza credenziali di accesso incorporate. I dettagli opzionali omessi li cancellano. Inviare `expectedRevision` dall'ordine corrente. Esiste un record di consegna per ordine; le integrazioni con i corrieri e le spedizioni divise non sono incluse. I dettagli del corriere vuoti non implicano mai la consegna propria.

## Elenco degli ordini

**GET** `/api/v1/orders`

Ordini più recenti per primi. L'elenco copre gli **ultimi 200 ordini** che corrispondono ai filtri. Per raccogliere nuovi ordini, effettuare polling con `status=new`.

| Nome       | Tipo    | Obbligatorio | Descrizione                             |
| ---------- | ------- | ------------ | --------------------------------------- |
| `status`   | stringa | no           | Uno dei seguenti stati.                 |
| `customer` | stringa | no           | ID cliente o codice account.            |
| `limit`    | intero  | no           | Da 1 a 100. Valore predefinito 100.     |
| `offset`   | intero  | no           | Righe da saltare. Valore predefinito 0. |

```bash
curl "https://tradecatalog.app/api/v1/orders?status=new" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/orders?status=new', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: orders } = await res.json()
```

```json
{
  "data": [
    {
      "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
      "reference": "SO-000142",
      "status": "new",
      "total": 31460,
      "currency": "GBP",
      "poNumber": "PO-88231",
      "createdAt": "2026-09-27T07:48:12.000Z",
      "customer": "Oakfield Joinery"
    }
  ],
  "nextOffset": null
}
```

| Campo       | Tipo          | Descrizione                                                                        |
| ----------- | ------------- | ---------------------------------------------------------------------------------- |
| `id`        | string        | ID dell'ordine.                                                                    |
| `reference` | string        | Numero d'ordine che i compratori vedono, come `SO-000142`.                         |
| `status`    | string        | Vedi [stati](#order-statuses).                                                     |
| `total`     | integer       | Totale dell'ordine come prezzato (vedi `taxLabel`), in unità minori di `currency`. |
| `currency`  | string        | Fissato quando l'ordine è stato effettuato.                                        |
| `poNumber`  | string o null | Il numero del foglio d'ordine del compratore.                                      |
| `createdAt` | string        | Quando è stato effettuato.                                                         |
| `customer`  | string        | Nome del cliente. Ottieni il codice account da `GET /orders/{id}`.                 |

## Ottieni un ordine

**GET** `/api/v1/orders/{id}`

L'ordine con le sue linee e la cronologia dello stato.

| Nome | In   | Tipo   | Obbligatorio | Descrizione     |
| ---- | ---- | ------ | ------------ | --------------- |
| `id` | path | string | sì           | ID dell'ordine. |

```bash
curl https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: order } = await res.json()
```

```json
{
  "data": {
    "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
    "customerAccountId": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "placedBy": "Fq3L0cZ7tN1pW9aYx2Rk",
    "reference": "SO-000142",
    "poNumber": "PO-88231",
    "notes": "Deliver to the Hebden Road site, gate 2.",
    "status": "new",
    "cancelReason": null,
    "currency": "GBP",
    "total": 31460,
    "createdAt": "2026-09-27T07:48:12.000Z",
    "updatedAt": "2026-09-27T07:48:12.000Z",
    "customer": { "name": "Oakfield Joinery", "accountCode": "OAK01" },
    "lines": [
      {
        "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "orderId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
        "productId": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
        "sku": "HX-8510",
        "name": "Hex bolt M10 x 50 zinc",
        "unitLabel": "Box of 100",
        "qty": 20,
        "unitPrice": 1573,
        "listPrice": 1850,
        "priceLabel": "Brand discount −15%",
        "lineTotal": 31460
      }
    ],
    "events": [
      {
        "status": "new",
        "note": null,
        "createdAt": "2026-09-27T07:48:12.000Z",
        "actor": "Sam Patel"
      }
    ]
  }
}
```

| Campo                                     | Descrizione                                                                                                                                                                                 |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerAccountId`                       | L'id del cliente.                                                                                                                                                                           |
| `placedBy`                                | Id della persona che l'ha inserito. `source` distingue gli ordini dei clienti da quelli del personale.                                                                                      |
| `notes`                                   | Note di consegna o d'ordine del compratore.                                                                                                                                                 |
| `cancelReason`                            | Perché è stato annullato, se lo è stato.                                                                                                                                                    |
| `lines`                                   | Cosa è stato ordinato a quale prezzo. Una fotografia istantanea: i cambi di prezzo successivi non lo modificano mai. `productId` è `null` se il prodotto è stato eliminato successivamente. |
| `lines[].priceLabel`                      | La regola che ha impostato il prezzo, come su [Prezzi](/docs/api/prices#the-price-object).                                                                                                  |
| `lines[].priceLabel` è `Price on request` | Il prodotto non ha un prezzo: `unitPrice` e `lineTotal` sono `0` e la riga non è in `total`. Il fornitore conferma il suo prezzo.                                                           |
| `events`                                  | Cronologia dello stato, dal più vecchio al più recente. `actor` è il nome della persona, o `null`.                                                                                          |

## Aggiorna o correggi un ordine

**PATCH** `/api/v1/orders/{id}`

Modifica lo stato di un ordine. Restituisce l'ordine aggiornato nella stessa struttura di `GET /orders/{id}`.

- I normali spostamenti in avanti seguono i fasi di esecuzione sopra. Altri spostamenti richiedono un motivo di correzione privato; uno stato corrente ripetuto non apporta alcuna modifica.
- L'annullamento richiede un `note`, che viene mostrato al compratore come motivo.
- I cambi di stato possono inviare email ai contatti del cliente, inclusa la nota pubblica. Una email generata non dimostra la consegna.
- La modifica viene registrata nella cronologia dell'ordine contro la persona che ha effettuato la chiave.

| Nome     | In   | Tipo   | Obbligatorio | Descrizione                                               |
| -------- | ---- | ------ | ------------ | --------------------------------------------------------- |
| `id`     | path | string | sì           | ID dell'ordine.                                           |
| `status` | body | string | sì           | Nuovo stato.                                              |
| `note`   | body | string | no           | Fino a 1.000 caratteri. Richiesto durante l'annullamento. |

```bash
curl -X PATCH https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "dispatched", "note": "DPD tracking 1554 2210 0931" }'
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'acknowledged' }),
})
const { data: order } = await res.json()
```

| Stato | Codice               | Quando                                                                                                                                    |
| ----- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| 400   | `invalid_request`    | Il movimento non è consentito, ad esempio "Un ordine ricevuto non può essere contrassegnato come completato", o annullare senza una nota. |
| 403   | `insufficient_scope` | La chiave è in sola lettura.                                                                                                              |
| 404   | `not_found`          | Nessun ordine di questo tipo in questo ambiente di lavoro.                                                                                |

Ripetere un `PATCH` che è già riuscito restituisce `400 invalid_request`, poiché l'ordine è avanzato. Verifica lo stato dell'ordine prima di considerarlo un fallimento.

## Non disponibile

L'API non può effettuare ordini per un compratore, o modificare le linee, le quantità o i prezzi di un ordine. I compratori effettuano gli ordini nel loro portale commerciale; le modifiche a un ordine vengono concordate con il cliente e fatturate nel vostro sistema.

Le liste degli ordini includono anche `paymentStatus`: null per termini di conto ordinario, o lo stato di pagamento Stripe opzionale. Lo stato di evasione è separato e non dimostra mai il pagamento. La pagina dettagliata dell'ordine è il posto dove verificare l'importo finale confermato e approvare il pagamento. L'API non può creare o approvare un pagamento.

`internalNote` (2.000 caratteri) e `correctionReason` (1.000 caratteri) sono campi di storia riservati al personale. `note` rimane pubblico, comprese le vecchie note. Inviare `expectedRevision` per rifiutare una modifica concorrente con una richiesta non valida. Le letture dell'API del personale includono la storia privata; le letture degli strumenti per acquirenti e del portale escludono le identità del personale e i campi privati. L'annullamento richiede comunque un motivo pubblico e il ritiro o il rimborso completo di un pagamento non ancora effettuato.
