# Preços

> O que um cliente paga por um SKU em uma quantidade, e exatamente qual conjunto de regras esse preço.

O TradeCatalog calcula todos os preços dos clientes em um só lugar, da mesma forma para o app, o portal comercial, o MCP e a API. Você nunca calcula preços manualmente.

Produtos configurados, taxas medidas, limites de quantidade, extras líquidos e arredondamento explícito também usam este serviço de preços. Cite uma linha completa com `POST /products/{id}/quote`; um preço ausente pode significar `configurationRequired`, enquanto uma especificação não precificada válida é Preço sob consulta. Use o total da linha retornado. Veja [configuração do catálogo de compras](/docs/guides/trade-catalogues) para precedência, tratamento líquido e publicação.

## Como os preços são calculados

A regra mais específica vence, e **as ajustes nunca são acumuladas**:

1. **Preço acordado.** Se o cliente tem um preço fixo para o produto, esse é o preço.
2. Caso contrário, o **preço da lista com uma porcentagem**, a primeira que se aplica. Uma porcentagem é um desconto (deduzido) ou um acréscimo (adicionado):
   1. a porcentagem do cliente para esse **produto**
   2. a porcentagem do cliente para a **categoria** do produto (a mais próxima vence)
   3. a porcentagem do cliente para a **marca** do produto
   4. a porcentagem do cliente sobre a **lista inteira**
3. Caso contrário, o **preço da lista**.

Um desconto de categoria de 10% e um acréscimo de lista de 5% dão 10% de desconto, não os dois. A unidade nunca é inferior a zero e é arredondada para cima até a unidade menor inteira. O total da linha é `unit × qty`.

Preços da lista e preços acordados são versões por data. A API sempre fornece o preço válido **agora**. Preços futuros de uma mudança agendada entram em vigor a partir da data.

## Listas de preços e acesso ao catálogo

Standard é o catálogo mestre do fornecedor. Outras listas de preços podem conter produtos com preços e produtos aguardando cotação. A ausência de preço em uma lista atribuída não retorna para Standard.

No espaço de trabalho, abra **Listas de preços** e escolha uma lista nomeada. **Clientes atribuídos podem ver** tem duas opções:

- **Todos os produtos do catálogo permitidos** é o padrão. A lista define os preços; os compradores atribuídos também podem navegar por outros produtos permitidos, conforme solicitado por preço.
- **Apenas os produtos desta lista** limita o catálogo, buscas, links de produtos e novos pedidos de cada comprador atribuído a apenas os produtos desta lista. A visibilidade de outros produtos e restrições de lista aprovada ainda se aplicam. Os clientes em outras listas mantêm seu acesso existente.

Cada lista mostra sua contagem de clientes, produtos com preço, produtos sem preço e total de produtos. Totais incluem produtos inativos, portanto podem exceder o que um comprador vê atualmente. Um preço zero é considerado como produto com preço. Um preço futuro é considerado como produto sem preço até sua data de início.

Para adicionar produtos, selecione-os em **Produtos**, escolha **Adicionar à lista de preços**, depois copie os preços Padrão com uma porcentagem opcional ou selecione **Adicionar sem preços**. Membros existentes mantêm seus preços. Produtos sem preço Padrão são adicionados como Preço sob consulta. Na página de uma lista nomeada, salvar um SKU com preço em branco o adiciona sem preço ou limpa seu preço atual. **Remover** o retira dessa lista, mantendo o produto principal e os pedidos anteriores. Resolva qualquer alteração de preço pendente para esse produto primeiro.

Estes controles exigem o plano Pro e direitos de edição de preços globais. Listas existentes mantêm seu acesso ao catálogo até que um fornecedor o altere. Importações via API para uma lista nomeada incluem linhas ordinárias de produtos com nenhum preço como membros sem preço; uma célula vazia em uma importação apenas de preços deixa o acesso inalterado.

## O objeto de preço

Cada produto’s `price` tem esta forma. `GET /prices` retorna os mesmos campos no nível superior de `data`.

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

| Campo        | Tipo                   | Descrição                                                                                                                                              |
| ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `unit`       | inteiro ou nulo        | Preço para uma unidade, em unidades menores. `null` se o produto não tiver preço.                                                                      |
| `line`       | inteiro ou nulo        | `unit × qty`.                                                                                                                                          |
| `list`       | inteiro ou nulo        | O preço lista atual.                                                                                                                                   |
| `discountBp` | inteiro                | O ajuste aplicado, em pontos básicos. `0` para preços acordados e lista.                                                                               |
| `adjustment` | `discount` ou `markup` | Como `discountBp` move o preço lista. `discount` quando não há porcentagem.                                                                            |
| `source`     | string                 | Qual conjunto de regras definiu o preço: veja abaixo.                                                                                                  |
| `label`      | string                 | Texto que os compradores veem ao lado do preço.                                                                                                        |
| `remark`     | string ou nulo         | A nota de preço acordado quando esse cliente tem permissão para vê-la. Caso contrário `null`. A nota interna exclusiva para a equipe nunca é incluída. |

| `source`   | Significado                   | Exemplo `label`                         |
| ---------- | ----------------------------- | --------------------------------------- |
| `fixed`    | O preço acordado pelo cliente | Preço acordado                          |
| `product`  | Porcentagem em um produto     | 10% de desconto no seu preço de lista   |
| `category` | Porcentagem em uma categoria  | 12,5% de desconto no seu preço de lista |
| `brand`    | Porcentagem em uma marca      | 15% acima do seu preço de lista         |
| `account`  | Porcentagem em toda a lista   | 10% acima do seu preço de lista         |
| `list`     | Preço de lista, sem ajuste    | (vazio)                                 |
| `unpriced` | O produto ainda não tem preço | (vazio)                                 |

## Verificar preço do cliente

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

O que o cliente paga por um SKU em uma quantidade, e por quê. Use para responder "Quanto Oakfield paga por 10 caixas do HX-8510?".

| Nome       | Tipo    | Obrigatório | Descrição                               |
| ---------- | ------- | ----------- | --------------------------------------- |
| `customer` | string  | sim         | ID do cliente ou código de conta.       |
| `sku`      | string  | sim         | SKU, ignorando maiúsculas e minúsculas. |
| `qty`      | inteiro | não         | 1 a 1.000.000. Padrão 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"
}
```

| Estado | Código             | Quando                                                   |
| ------ | ------------------ | -------------------------------------------------------- |
| 400    | `validation_error` | `customer` ou `sku` ausente, ou `qty` fora do intervalo. |
| 404    | `not_found`        | Não existe cliente, ou nenhum produto com esse SKU.      |

Este ponto de acesso verifica apenas o preço. Ele não verifica se o cliente pode ver o produto. Para isso, use [`GET /products/{id}?customer=…`](/docs/api/products#get-one-product).

## Preços para muitos produtos

Para precificar uma página inteira de produtos para um cliente, use `GET /products?customer=OAK01`. Cada linha’s `price` é o preço desse cliente, e os produtos exclusivos para outros clientes são omitidos. Veja [Produtos](/docs/api/products#list-or-search-products).

## Alterando preços

- **Preços da lista** mudam por meio de alterações de preços, então os clientes recebem notificação. Envie novos preços com [`PUT /products`](/docs/api/products#create-or-update-products); eles entram em um rascunho que você publica no aplicativo.
- **Margens, descontos e preços acordados** são definidos por cliente no aplicativo. Leia-os com [`GET /customers/{id}`](/docs/api/customers#get-one-customer).
- **Preços acordados** para muitos clientes são definidos com [`PUT /agreed-prices`](/docs/api/prices#set-agreed-prices).
- O **percentual da lista inteira** de um cliente pode ser definido com [`PUT /customers`](/docs/api/customers#create-or-update-customers) (`discountBp`, e opcional `adjustment`).

## Definir preços acordados

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

Crie ou substitua preços acordados para clientes e produtos existentes. Envie até 2.000 linhas. Os preços são unidades menores inteiras. Deixe `commit` como falso para visualizar: nada é salvo e cada conta ou SKU desconhecida retorna como erro de linha. Defina `commit` como verdadeiro para salvar. Salvar fecha o preço acordado válido agora e insere o novo a partir deste momento, em lotes de 100. Apenas para Pro.

| Nome          | Tipo     | Obrigatório | Descrição                                                             |
| ------------- | -------- | ----------- | --------------------------------------------------------------------- |
| `commit`      | booleano | não         | `true` salva. Padrão `false` apenas prévias.                          |
| `prices`      | array    | sim         | 1 a 2.000 preços.                                                     |
| `accountCode` | string   | sim         | Código da conta do cliente, sem distinção de maiúsculas e minúsculas. |
| `sku`         | string   | sim         | SKU do produto, sem distinção de maiúsculas e minúsculas.             |
| `price`       | inteiro  | sim         | Preço acordado em unidades menores.                                   |
| `note`        | string   | não         | Até 200 caracteres, exibido para o pessoal.                           |

```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"}]}'
```

| Status | Código             | Quando                                                              |
| ------ | ------------------ | ------------------------------------------------------------------- |
| 400    | `validation_error` | Um preço está faltando, negativo ou acima do limite.                |
| 403    | `read_only`        | O espaço de trabalho não está no Pro, ou a chave é somente leitura. |
