GetTax, powered by SAC Fiscal

Conceitos

Boas práticas

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

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.
JavaScript
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.
C#
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.
  • Configure timeout no seu cliente HTTP e repita só 500 e 503, com backoff. Ver Erros e novas tentativas.
  • Trate enums como abertos: novos setores e componentes podem surgir. Leia os setores de GET /v1/enums/sectors.
  • Ignore campos que você não conhece na resposta, em vez de falhar.