# Boas práticas

O que guardar, como tratar valores fiscais e como integrar o GetTax a um emissor de documentos fiscais.

Versão HTML: https://kind-coast-062131010.2.azurestaticapps.net/guias/boas-praticas/

## Envie a operação real

- Envie o CFOP da operação que vai ser emitida. Não use um CFOP de venda como padrão para tudo: devolução, transferência e remessa podem ter outro CST.
- Envie `sector` quando souber o setor do seu cliente final. Sem setor, vêm todas as alternativas, de qualquer setor.
- Valide o dígito verificador do GTIN antes de enviar. Um GTIN inválido não consome franquia, mas custa uma ida e volta.

## Trate os quatro status

- Só `CLASSIFIED` é resultado concluído. `IN_REVIEW` pode trazer alternativas calculadas, mas não deve ser emitido automaticamente.
- Sem classificação concluída, não presuma tributação integral nem preencha alíquotas por conta própria.
- Com mais de uma alternativa, a escolha é do emissor, pela condição em `applicability`. Nunca pela posição na lista.

```node
const res = await fetch(`${process.env.GETTAX_URL}/v1/classifications/resolve`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.GETTAX_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ gtin: "7891000315507", cfop: "5102", sector: "VAREJO_GERAL" }),
});

if (!res.ok) {
  const { error } = await res.json(); // { "error": "INVALID_REQUEST" }, etc.
  throw new Error(`GetTax ${res.status}: ${error}`);
}

const decision = await res.json();

switch (decision.status) {
  case "CLASSIFIED":
    // Todas as alternativas pertinentes. A ordem não é recomendação:
    // com mais de uma, o emissor escolhe pela condição em `applicability`.
    await saveDecision(decision);
    break;
  case "IN_REVIEW":
    // Pode trazer alternativas, mas o resultado não está concluído.
    await sendToReview(decision);
    break;
  case "PENDING":
    // Produto fora do catálogo. Consulte de novo mais tarde, sem loop.
    await retryLater(decision.gtin, decision.retryAfterSeconds);
    break;
  case "NOT_FOUND":
    // A GS1 não conhece o GTIN: confira o cadastro do item.
    await flagInvalidGtin(decision.gtin);
    break;
}
```

## Guarde a decisão com o documento

Junto de cada documento fiscal emitido, guarde:

- `decisionVersion`: identifica a decisão na trilha de auditoria do GetTax.
- `referenceDate`, `cfop` e `sector` usados na consulta.
- Da alternativa aplicada: `cst`, `cClassTrib`, `rule.id`, `rule.version`, `taxClassification.sourceVersion` e os `sourceVersion` de `rates[]`.
- Os valores de `components[]` como vieram.

É o que permite explicar depois de onde veio cada número, mesmo que a regra ou a alíquota mude.

`decisionVersion` é novo a cada consulta, mesmo com a mesma entrada e o mesmo resultado. Ele identifica a consulta, não a classificação: para saber se a classificação mudou, compare `cst`, `cClassTrib`, `rule.id` e `rule.version`.

## Valores fiscais como decimal

Alíquotas e reduções chegam como strings de fração decimal (`"0.009000"` = 0,9%). Mantenha a precisão:

- Converta direto para um tipo decimal exato e guarde como decimal ou texto. Nunca passe por `float` ou `double`.
- Não converta para percentual inteiro antes de guardar. Se precisar exibir, formate só na tela.
- Recalcule, se quiser conferir, com a fórmula `effectiveRate = standardRate × (1 − reductionPct)`, em decimal.

```csharp
using System.Globalization;

// "0.003600" -> 0.003600m, sem passar por double
var effectiveRate = decimal.Parse(component.GetProperty("effectiveRate").GetString()!, CultureInfo.InvariantCulture);
```

```python
from decimal import Decimal

effective_rate = Decimal(component["effectiveRate"])  # Decimal('0.003600')
```

Em JavaScript, `Number("0.003600")` é ponto flutuante. Mantenha a string ou use uma biblioteca decimal.

## Reaproveite as respostas

A classificação depende de GTIN, CFOP, setor e data de referência, não do seu cliente final. Guarde a resposta no seu banco e reutilize entre os seus clientes:

- Use como chave o GTIN de 14 dígitos, o CFOP e o setor (ou a ausência dele).
- A decisão vale enquanto a data de referência estiver dentro da vigência de tudo o que ela usou: o menor `effectiveTo` entre a regra, a linha de cClassTrib e as alíquotas. `null` quer dizer sem término previsto.
- A curadoria publica regras e versões novas. Reconsulte periodicamente, e sempre que a data passar de um `effectiveTo`.
- Não guarde em cache `PENDING` nem `IN_REVIEW` como resultado final.

## Escolha a rota certa

- **Consulta unitária** para o dia a dia: item novo no cadastro, conferência antes da emissão. Pode buscar na GS1 um produto que ainda não está no catálogo, quando a busca está ativa no ambiente, por isso pode levar alguns segundos a mais nesse caso.
- **Lote** para carga e revisão de catálogo: até 100 itens por requisição, sem busca na GS1. Itens `PENDING` do lote podem ser reconsultados depois, pela consulta unitária.
- **Lista de setores** para validar a chave e checar a saúde da integração, sem consumir franquia.

## Integração robusta

- Chame a API pelo seu backend e guarde a chave num cofre de segredos. Ver [Autenticação](https://kind-coast-062131010.2.azurestaticapps.net/guias/autenticacao.md).
- Configure timeout no seu cliente HTTP e repita só `500` e `503`, com backoff. Ver [Erros e novas tentativas](https://kind-coast-062131010.2.azurestaticapps.net/guias/erros.md#quando-repetir).
- Trate enums como abertos: novos setores e componentes podem surgir. Leia os setores de [`GET /v1/enums/sectors`](https://kind-coast-062131010.2.azurestaticapps.net/referencia/list-sectors.md).
- Ignore campos que você não conhece na resposta, em vez de falhar.
