# Clienti

> Elenca i clienti commerciali, leggi uno sconto del cliente, i prezzi concordati e i compratori, e crea o aggiorna i clienti.

Un cliente è un conto commerciale: un'azienda a cui vendi, con un codice account, uno sconto account opzionale e le persone (acquirenti) che possono accedere per effettuare ordini per conto loro.

## L'oggetto cliente

```json
{
  "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
  "accountCode": "OAK01",
  "name": "Oakfield Joinery",
  "legalName": "Oakfield Joinery Ltd",
  "email": "accounts@oakfieldjoinery.co.uk",
  "discountBp": 500,
  "active": true,
  "newsOptOut": false,
  "catalogueScope": "all",
  "createdAt": "2026-07-02T10:12:44.000Z"
}
```

| Campo            | Tipo           | Descrizione                                                                                                                                                              |
| ---------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`             | stringa        | ID del cliente.                                                                                                                                                          |
| `accountCode`    | stringa        | Il tuo codice account. Unico nello spazio di lavoro, ignorando le maiuscole. Può essere modificato nell'app, quindi conserva il `id` per qualsiasi cosa a lungo termine. |
| `name`           | stringa        | Nome commerciale.                                                                                                                                                        |
| `legalName`      | stringa o null | Nome registrato, quando diverso.                                                                                                                                         |
| `email`          | stringa o null | Email principale del contatto, in minuscolo.                                                                                                                             |
| `discountBp`     | intero o null  | Percentuale del cliente sul listino completo, in punti base. `null` per nessuna.                                                                                         |
| `adjustment`     | stringa        | `discount` (rimosso dal listino) o `markup` (aggiunto). I clienti esistenti sono `discount`.                                                                             |
| `active`         | booleano       | I clienti inattivi non possono accedere o effettuare ordini.                                                                                                             |
| `newsOptOut`     | booleano       | Il cliente ha disattivato le email delle offerte e delle notizie. Le email sui cambiamenti dei prezzi vengono comunque inviate.                                          |
| `catalogueScope` | stringa        | `all` (vede l'intero catalogo) o `approved` (vede e ordina solo il proprio elenco approvato). Impostato nella pagina del cliente; solo lettura nell'API.                 |
| `createdAt`      | stringa        | Quando il cliente è stato aggiunto.                                                                                                                                      |

Ovunque l'API richiede un cliente, puoi usare il `id` o il `accountCode`.

## Elenco clienti

**GET** `/api/v1/customers`

Tutti i clienti, ordinati per nome.

| Nome     | Tipo   | Obbligatorio | Descrizione                  |
| -------- | ------ | ------------ | ---------------------------- |
| `limit`  | intero | no           | 1 a 100. Default 100.        |
| `offset` | intero | no           | Righe da saltare. Default 0. |

```bash
curl "https://tradecatalog.app/api/v1/customers?limit=100" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

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

```json
{
  "data": [
    {
      "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
      "accountCode": "OAK01",
      "name": "Oakfield Joinery",
      "legalName": "Oakfield Joinery Ltd",
      "email": "accounts@oakfieldjoinery.co.uk",
      "discountBp": 500,
      "active": true,
      "newsOptOut": false,
      "catalogueScope": "all",
      "createdAt": "2026-07-02T10:12:44.000Z"
    }
  ],
  "nextOffset": null
}
```

## Ottieni un cliente

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

Il cliente più tutto ciò che influisce sui loro prezzi, e chi può accedere per loro.

| Nome | In   | Tipo    | Obbligatorio | Descrizione                      |
| ---- | ---- | ------- | ------------ | -------------------------------- |
| `id` | path | stringa | sì           | ID del cliente o codice account. |

```bash
curl https://tradecatalog.app/api/v1/customers/OAK01 \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

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

```json
{
  "data": {
    "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "accountCode": "OAK01",
    "name": "Oakfield Joinery",
    "legalName": "Oakfield Joinery Ltd",
    "email": "accounts@oakfieldjoinery.co.uk",
    "discountBp": 500,
    "priceListId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "priceList": "Standard",
    "active": true,
    "newsOptOut": false,
    "createdAt": "2026-07-02T10:12:44.000Z",
    "discountRules": [
      {
        "id": "d0e1f2a3-4b5c-4d6e-9f7a-8b9c0d1e2f3a",
        "scope": "product",
        "discountBp": 2000,
        "productId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
        "sku": "HX-8510",
        "target": "Hex bolt M10 x 50 zinc",
        "list": 2280,
        "unit": 1824
      },
      {
        "id": "e1f2a3b4-5c6d-4e7f-8a9b-0c1d2e3f4a5b",
        "scope": "brand",
        "discountBp": 1500,
        "productId": null,
        "sku": null,
        "target": "Northgate",
        "list": null,
        "unit": null
      },
      {
        "id": "f2a3b4c5-6d7e-4f80-9a1b-2c3d4e5f6a7b",
        "scope": "category",
        "discountBp": 1250,
        "productId": null,
        "sku": null,
        "target": "Hinges",
        "list": null,
        "unit": null
      }
    ],
    "agreedPrices": [
      {
        "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "productId": "6c7d8e9f-0a1b-4c2d-9e3f-4a5b6c7d8e9f",
        "price": 1450,
        "note": "Contract price to March 2027",
        "noteVisible": true,
        "internalNote": "Agreed with their buyer",
        "sku": "HX-8512",
        "name": "Hex bolt M12 x 50 zinc",
        "list": 1980,
        "unit": 1450
      }
    ],
    "buyers": [{ "name": "Sam Patel", "email": "sam@oakfieldjoinery.co.uk" }],
    "pendingInvitations": [{ "email": "orders@oakfieldjoinery.co.uk" }]
  },
  "currency": "GBP"
}
```

| Campo                | Descrizione                                                                                                                                                                                                                                                                   |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `priceList`          | Nome del listino dei prezzi da cui questo cliente acquista. `priceListId` è l'id di quel listino. Un prodotto senza prezzo sul listino ha prezzo su richiesta.                                                                                                                |
| `discountRules`      | Percentuali per prodotto, categoria e marca. `scope` è `product`, `category` o `brand`; `adjustment` è `discount` o `markup`; `target` è il suo nome. Le regole dei prodotti forniscono anche `sku`, `list` e `unit` (ciò che pagano).                                        |
| `agreedPrices`       | Prezzi concordati (fissi) validi ora, per SKU, in unità minori, con il prezzo `list` corrente e il prezzo `unit` che pagano. `note` è il commento; `noteVisible` indica se quel cliente lo vede. `internalNote` è riservato al personale e non viene mai mostrato al cliente. |
| `buyers`             | Persone che possono accedere e effettuare ordini per questo cliente.                                                                                                                                                                                                          |
| `pendingInvitations` | Inviti inviati ma non ancora accettati, e non scaduti.                                                                                                                                                                                                                        |

Vedi [come vengono calcolati i prezzi](/docs/api/prices#how-prices-are-worked-out) per capire come si combinano.

## Crea o aggiorna clienti

**PUT** `/api/v1/customers`

Invia fino a **100 clienti** per chiamata. Ogni riga viene abbinata a un cliente esistente in questo modo:

1. Se `accountCode` è impostato, per codice account. Nessun abbinamento significa un nuovo cliente con quel codice.
2. Altrimenti, se `email` è impostato, per email. Nessun abbinamento significa un nuovo cliente.
3. Altrimenti per nome, ignorando la maiuscola.

I nuovi clienti senza codice account ne ricevono uno creato dal loro nome.

### Corpo

| Campo                     | Tipo                  | Obbligatorio | Descrizione                                                                                                                                                                                       |
| ------------------------- | --------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customers`               | array                 | sì           | Da 1 a 100 clienti. I codici di account devono essere unici all'interno della chiamata.                                                                                                           |
| `customers[].accountCode` | stringa o null        | sì           | Fino a 50 caratteri. `null` per effettuare una corrispondenza tramite email o nome.                                                                                                               |
| `customers[].name`        | stringa               | sì           | Nome commerciale, fino a 160 caratteri.                                                                                                                                                           |
| `customers[].email`       | stringa o null        | sì           | Email di contatto principale. `null` la cancella.                                                                                                                                                 |
| `customers[].discountBp`  | intero o null         | sì           | Percentuale dell'intero elenco, da 0 a 10.000, per uno sconto o un aumento. `null` la cancella.                                                                                                   |
| `customers[].adjustment`  | `discount` o `markup` | no           | In quale modo `discountBp` sposta il prezzo. Ometterlo per lasciare la direzione attuale. I nuovi clienti predefiniti a `discount`.                                                               |
| `customers[].priceList`   | stringa o null        | no           | Nome di un elenco prezzi esistente. Ometterlo per lasciare invariato l'elenco. `null` posiziona il cliente su Standard. Un nome sconosciuto viene rifiutato.                                      |
| `customers[].region`      | stringa o null        | no           | Nome di una regione esistente. Ometterlo per lasciare invariata la regione. `null` la cancella. Un nome sconosciuto viene rifiutato.                                                              |
| `customers[].rep`         | stringa o null        | no           | Email o nome utente di un dipendente assegnato a questo cliente. Ometterlo per lasciare invariato il rappresentante. `null` lo cancella. Un'email o un nome utente sconosciuti vengono rifiutati. |

`accountCode`, `name`, `email` e `discountBp` sono obbligatori e vengono scritti come inviati: `null` cancella `email` e `discountBp`. In questo modo, un campo mancante nel tuo codice non può lasciare invariata una percentuale precedente. `adjustment` e `priceList` sono opzionali: lascia `adjustment` fuori per mantenere un aumento o uno sconto come è.

La tabella dei clienti ha la stessa scelta. Accanto a Sconto % può avere una colonna di Adattamento (`discount` o `markup`), una colonna di Elenco prezzi e una colonna di Rappresentante. La cella del rappresentante è l'email di quella persona, o il loro nome utente quando si registrano senza email. Una cella vuota lascia quel campo come è. Un nome di elenco sconosciuto o un rappresentante sconosciuto è un errore in quella riga.

Aggiornare un cliente non invia email a nessuno o non invita i compratori. Invita i compratori dalla pagina del cliente nell'app. Il nome registrato, lo stato attivo, le regole dello sconto e i prezzi concordati vengono modificati anche nell'app.

```bash
curl -X PUT https://tradecatalog.app/api/v1/customers \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customers": [
      {
        "accountCode": "OAK01",
        "name": "Oakfield Joinery",
        "email": "accounts@oakfieldjoinery.co.uk",
        "discountBp": 500
      },
      {
        "accountCode": "BRK07",
        "name": "Brookside Builders",
        "email": null,
        "discountBp": null
      }
    ]
  }'
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/customers', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ customers }),
})
const { data } = await res.json()
console.log(`${data.created} new, ${data.updated} updated`)
```

```json
{
  "data": { "created": 1, "updated": 1 }
}
```

Il cambiamento appare nel registro delle attività come nome della chiave.
