# Prezzi

> Quanto paga un cliente per un SKU in una quantità, e esattamente quale insieme di regole applica quel prezzo.

TradeCatalog calcola ogni prezzo cliente in un unico posto, nello stesso modo per l'app, il portale commerciale, MCP e l'API. Non calcoli mai i prezzi tu stesso.

Prodotti configurati, tariffe misurate, soglie di quantità, extra netti e arrotondamenti espliciti utilizzano anche questo servizio di prezzo. Citare una riga completa con `POST /products/{id}/quote`; una mancanza di prezzo potrebbe significare `configurationRequired`, mentre una specifica non prezzata valida è "Prezzo su richiesta". Utilizzare il totale della riga restituito. Vedere [setup del catalogo commerciale](/docs/guides/trade-catalogues) per la precedenza, il trattamento netto e la pubblicazione.

## Come vengono calcolati i prezzi

La regola più specifica vince, e **le modifiche non si accumulano**:

1. **Prezzo concordato.** Se il cliente ha un prezzo (fisso) concordato per il prodotto, è questo il prezzo.
2. In caso contrario il **prezzo di listino con un percentuale**, la prima che si applica. Una percentuale è uno sconto (dedotto) o un markup (aggiunto):
   1. la percentuale del cliente per quel **prodotto**
   2. la percentuale del cliente per la **categoria** del prodotto (vince la più vicina)
   3. la percentuale del cliente per la **marca** del prodotto
   4. la percentuale del cliente sul **listino completo**
3. In caso contrario il **prezzo di listino**.

Uno sconto su categoria del 10% e un markup su lista intera del 5% danno uno sconto del 10%, non entrambi. L'unità non è mai inferiore a zero e viene arrotondata per eccesso a un'unità minima intera. Il totale della riga è `unit × qty`.

I prezzi di listino e i prezzi concordati sono versionati in base alla data. L'API fornisce sempre il prezzo valido **ora**. I prezzi futuri da un cambiamento programmato si applicano dalla loro data.

## Elenco prezzi e accesso al catalogo

Standard è il catalogo principale del fornitore. Altri elenchi prezzi possono contenere prodotti con prezzi e prodotti in attesa di un preventivo. Una mancanza di prezzo in un elenco assegnato non si ripristina su Standard.

Nello spazio di lavoro, apri **Elenco prezzi** e scegli un elenco denominato. **Clienti assegnati possono vedere** ha due opzioni:

- **Tutti i prodotti del catalogo autorizzati** è il predefinito. L'elenco stabilisce i prezzi; i compratori assegnati possono anche navigare tra altri prodotti autorizzati come Prezzo su richiesta.
- **Solo i prodotti in questo elenco** limita ogni catalogo, ricerche, collegamenti ai prodotti, download e nuovi ordini dei compratori assegnati a quei prodotti dell'elenco. Restano valide altre visibilità dei prodotti e restrizioni degli elenchi approvati. I clienti su altri elenchi mantengono l'accesso esistente.

Ogni elenco mostra il numero di clienti, i prodotti con prezzo, i prodotti senza prezzo e il totale dei prodotti. I totali includono i prodotti non attivi, quindi possono superare ciò che un compratore vede attualmente. Un prezzo zero è considerato come prezzo. Un prezzo futuro è considerato come non prezzo fino alla data di inizio.

Per aggiungere prodotti, selezionarli su **Prodotti**, scegliere **Aggiungi all'elenco prezzi**, quindi copiare i prezzi Standard con una percentuale opzionale o scegliere **Aggiungi senza prezzi**. I membri esistenti mantengono i loro prezzi. I prodotti senza prezzo Standard vengono aggiunti come Prezzo su richiesta. Nella pagina di un elenco denominato, salvare un SKU con un prezzo vuoto lo aggiunge senza prezzo o cancella il prezzo attuale. **Rimuovi** lo rimuove da quell'elenco, mantenendo il prodotto principale e gli ordini precedenti. Risolvere eventuali modifiche al prezzo in sospeso per quel prodotto prima.

Questi controlli richiedono il piano Pro e i diritti di modifica globale dei prezzi. Le liste esistenti mantengono l'accesso al catalogo fino a quando un fornitore non lo modifica. Gli import API in una lista denominata includono righe di prodotti ordinarie senza prezzo come membri non prezzati; una cella vuota in un importazione solo prezzi lascia invariato l'accesso.

## L'oggetto prezzo

Ogni prodotto ha questa struttura `price`. `GET /prices` restituisce gli stessi campi al livello principale di `data`.

```json
{
  "unit": 1573,
  "line": 15730,
  "list": 1850,
  "discountBp": 1500,
  "adjustment": "discount",
  "source": "brand",
  "label": "15% off your list",
  "remark": null
}
```

| Campo        | Tipo                  | Descrizione                                                                                                                                             |
| ------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unit`       | intero o null         | Prezzo per una unità, in unità minori. `null` se il prodotto non ha prezzo.                                                                             |
| `line`       | intero o null         | `unit × qty`.                                                                                                                                           |
| `list`       | intero o null         | Il prezzo listino attuale.                                                                                                                              |
| `discountBp` | intero                | L'aggiustamento applicato, in punti base. `0` per prezzi concordati e listino.                                                                          |
| `adjustment` | `discount` o `markup` | In quale modo `discountBp` modifica il prezzo listino. `discount` quando non c'è una percentuale.                                                       |
| `source`     | stringa               | Quale set di regole ha impostato il prezzo: vedere di seguito.                                                                                          |
| `label`      | stringa               | Testo che i compratori vedono accanto al prezzo.                                                                                                        |
| `remark`     | stringa o null        | La nota sul prezzo concordato quando quel cliente è autorizzato a vederla. Altrimenti `null`. La nota interna riservata al personale non è mai inclusa. |

| `source`   | Significato                          | Esempio `label`             |
| ---------- | ------------------------------------ | --------------------------- |
| `fixed`    | Il prezzo concordato dal cliente     | Prezzo concordato           |
| `product`  | Percentuale su un prodotto           | 10% di sconto sul listino   |
| `category` | Percentuale su una categoria         | 12,5% di sconto sul listino |
| `brand`    | Percentuale su un brand              | 15% di aumento sul listino  |
| `account`  | Percentuale su tutto il listino      | 10% di aumento sul listino  |
| `list`     | Prezzo listino, nessun aggiustamento | (vuoto)                     |
| `unpriced` | Il prodotto non ha ancora un prezzo  | (vuoto)                     |

## Verifica del prezzo del cliente

**GET** `/api/v1/prices`

Quanto paga un cliente per un SKU in una quantità, e perché. Usalo per rispondere a "Quanto paga Oakfield per 10 scatole di HX-8510?".

| Nome       | Tipo    | Obbligatorio | Descrizione                             |
| ---------- | ------- | ------------ | --------------------------------------- |
| `customer` | stringa | sì           | ID cliente o codice account.            |
| `sku`      | stringa | sì           | SKU, ignorando il caso.                 |
| `qty`      | intero  | no           | Da 1 a 1.000.000. Valore predefinito 1. |

```bash
curl "https://tradecatalog.app/api/v1/prices?customer=OAK01&sku=HX-8510&qty=10" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const params = new URLSearchParams({
  customer: 'OAK01',
  sku: 'HX-8510',
  qty: '10',
})
const res = await fetch(`https://tradecatalog.app/api/v1/prices?${params}`, {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: price, currency } = await res.json()
```

```json
{
  "data": {
    "customer": {
      "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
      "accountCode": "OAK01",
      "name": "Oakfield Joinery"
    },
    "sku": "HX-8510",
    "name": "Hex bolt M10 x 50 zinc",
    "qty": 10,
    "unit": 1573,
    "line": 15730,
    "list": 1850,
    "discountBp": 1500,
    "source": "brand",
    "label": "15% off your list",
    "remark": null
  },
  "currency": "GBP"
}
```

| Stato | Codice             | Quando                                                    |
| ----- | ------------------ | --------------------------------------------------------- |
| 400   | `validation_error` | `customer` o `sku` mancante, o `qty` fuori range.         |
| 404   | `not_found`        | Nessun cliente esistente, o nessun prodotto con quel SKU. |

Questo endpoint controlla solo il prezzo. Non controlla se il cliente può vedere il prodotto. Per questo, utilizzare [`GET /products/{id}?customer=…`](/docs/api/products#get-one-product).

## Prezzi per molti prodotti

Per prezzare una pagina intera di prodotti per un cliente, utilizzare `GET /products?customer=OAK01`. Ogni riga ha il `price` che è il prezzo di quel cliente e i prodotti esclusivi per altri clienti vengono esclusi. Vedere [Prodotti](/docs/api/products#list-or-search-products).

## Modifica dei prezzi

- **List prices** cambiano con i cambiamenti dei prezzi, quindi i clienti ricevono una notifica. Inviare nuovi prezzi con [`PUT /products`](/docs/api/products#create-or-update-products); vengono inseriti in un bozza che pubblichi nell'app.
- **Markup, sconti e prezzi concordati** vengono impostati per cliente nell'app. Leggili con [`GET /customers/{id}`](/docs/api/customers#get-one-customer).
- **Prezzi concordati** per molti clienti vengono impostati con [`PUT /agreed-prices`](/docs/api/prices#set-agreed-prices).
- Il **percentuale per l'intera lista** di un cliente può essere impostata con [`PUT /customers`](/docs/api/customers#create-or-update-customers) (`discountBp`, e `adjustment` opzionale).

## Impostare i prezzi concordati

**PUT** `/api/v1/agreed-prices`

Crea o sostituisci i prezzi concordati per clienti e prodotti esistenti. Invia fino a 2.000 righe. I prezzi sono unità minori intere. Lascia `commit` falso per il preview: nulla viene salvato, e ogni account o SKU sconosciuto torna come errore di riga. Imposta `commit` su true per salvare. Salvare chiude il prezzo concordato valido attualmente e inserisce il nuovo da questo momento, in lotti di 100. Solo Pro.

| Nome          | Tipo     | Obbligatorio | Descrizione                                        |
| ------------- | -------- | ------------ | -------------------------------------------------- |
| `commit`      | booleano | no           | `true` salva. Il predefinito `false` prevede solo. |
| `prices`      | array    | sì           | 1 a 2.000 prezzi.                                  |
| `accountCode` | stringa  | sì           | Codice account del cliente, ignorando il caso.     |
| `sku`         | stringa  | sì           | SKU del prodotto, ignorando il caso.               |
| `price`       | intero   | sì           | Prezzo concordato in unità minori.                 |
| `note`        | stringa  | no           | Fino a 200 caratteri, visibile al personale.       |

```bash
curl -X PUT https://tradecatalog.app/api/v1/agreed-prices \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"commit":false,"prices":[{"accountCode":"OAK01","sku":"HX-8510","price":1250,"note":"Contract"}]}'
```

| Stato | Codice             | Quando                                                           |
| ----- | ------------------ | ---------------------------------------------------------------- |
| 400   | `validation_error` | Un prezzo è mancante, negativo o superiore al limite.            |
| 403   | `read_only`        | Lo spazio di lavoro non è su Pro, o la chiave è in sola lettura. |
