v1.0.1 publicada no npm
Cálculo tributário brasileiro para JS/TS

Precisão decimal arbitrária para NF-e em JavaScript e TypeScript.
7 calculadores. Audit trail. Zero dependências.
A SEFAZ não rejeita.
Construído por quem já debugou rejeição 629 em produção.

npm version npm downloads CI
zero deps 95%+ coverage TypeScript strict Node 20 / 22 / 24 HALF_UP (SEFAZ)
Ver no GitHub

IEEE 754 não serve pra dinheiro. A SEFAZ não perdoa.

JavaScript usa ponto flutuante (IEEE 754). 0.1 + 0.2 não dá 0.3. Quando a NF-e diverge 1 centavo, a SEFAZ rejeita com erro 629/630. .toFixed(2) mascara, não resolve: o erro já acumulou nas operações intermediárias. Nota rejeitada = venda parada.

// JavaScript nativo (IEEE 754)
1.064 * 39680
// = 42219.520000000004
// .toFixed(2) mascara, mas o erro já propagou
// → SEFAZ: REJEITADO (erro 629). vProd ≠ vUnCom × qCom

// tributos-br (aritmética decimal sobre strings)
Decimal.from("1.064").mul("39680").toFixed(2)
// = "42219.52" (exato, sempre)
// → SEFAZ: AUTORIZADO ✓
629
vProd != vUnCom x qCom
630
vProd != vUnTrib x qTrib
já causa rejeição

Cálculo fiscal completo com a precisão que a SEFAZ exige

Funções puras. Recebe parâmetros, retorna resultado com audit trail de cada etapa. Stateless e auditável.

ICMS Próprio
Cálculo por dentro e por fora. Com e sem inclusão do imposto na base. Qualquer alíquota via parâmetro.
IPI
Cálculo por fora. Recebe valor do produto e alíquota, retorna imposto + audit trail. Use com calcSt() quando IPI compõe a base ST.
DIFAL
Base única (não-contribuinte, B2C) e base dupla (contribuinte, B2B, LC 190/2022). 100% destino desde 2019.
ST Unificada
Uma única calcSt() cobre 5 cenários via parâmetros: MVA, pauta fiscal, redução de base, FECOP.
MVA Ajustada
Fórmula SEFAZ com ALQ Intra + FECOP no denominador. Arredondamento em 4 casas decimais. Parâmetros puros, o chamador decide quando aplicar.
CBS / IBS 2026
Reforma tributária (LC 214/2025). Alíquotas de teste CBS 0,9% + IBS 0,1%. Imposto por fora. Campos NT 2025.002.

Exemplos que copiam e colam

Cada calculador recebe parâmetros, retorna resultado tipado com Decimal + audit trail. Sem hardcode de alíquotas.

import { calcIcms } from 'tributos-br'

const resultado = calcIcms({
  valorProduto: '1000',
  aliquota: '0.18',         // 18%
})

resultado.imposto.toFixed(2)  // '180.00'
resultado.base.toFixed(2)     // '1000.00'
resultado.audit              // [{ step: "Base ICMS", formula: "1000", value: "1000.00" }, ...]

// ICMS por dentro (imposto embutido no preço)
const porDentro = calcIcms({
  valorProduto: '1000',
  aliquota: '0.18',
  incluirImpostoNaBase: true,
})
porDentro.base.toFixed(2)    // '1219.51' (1000 / (1 - 0.18))
import { calcSt } from 'tributos-br'

// 5 cenários de ST unificados em 1 função
const resultado = calcSt({
  baseIcms: '1000',
  aliquotaIcms: '0.12',
  mva: '0.40',
  aliquotaSt: '0.18',
  valorIpi: '100',          // opcional - IPI compõe base ST
})

resultado.icmsSt.toFixed(2)   // '157.20' ICMS-ST a recolher
resultado.baseSt.toFixed(2)   // '1540.00' base de cálculo ST
resultado.audit              // [...passo a passo rastreável]
import { calcDifal } from 'tributos-br'

// Base dupla (LC 190/2022), contribuinte
const resultado = calcDifal({
  valorOperacao: '1000',
  aliquotaInterestadual: '0.12',
  aliquotaInternaDestino: '0.18',
  destinatarioContribuinte: true,  // ← muda o cálculo
})

resultado.difal               // Decimal, diferencial de alíquota
resultado.baseDifal.toFixed(2) // '1073.17' base recalculada "por dentro"
resultado.audit               // [...cada etapa documentada]
import { calcMvaAjustada } from 'tributos-br'

// MVA ajustada para operação interestadual
const resultado = calcMvaAjustada({
  mvaOriginal: '0.40',            // 40%
  aliquotaInterestadual: '0.12', // 12%
  aliquotaInterna: '0.18',        // 18%
  fecop: '0.02',                  // FECOP 2% (RJ, MG, CE...)
})

resultado.mvaAjustada.toFixed(4)  // '0.5400' (54%)
resultado.audit                    // [...fórmula com FECOP no denominador]

// Sem FECOP: ignorar gera ~4pp de diferença na MVA
// ALQ Intra Efetiva = ALQ Interna + FECOP
import { Decimal, RoundingMode } from 'tributos-br/precision'

// Entry point separado, use só a precisão sem os calculadores
const a = Decimal.from('1.064')
const b = Decimal.from('39680')

a.mul(b).toFixed(2)              // '42219.52' (exato, sempre)
a.add('0.2')                      // Decimal('1.264'), sem IEEE 754

// Atalhos monetários
Decimal.from('1.235').toMoney().toFixed(2)   // '1.24'
Decimal.from('0.12345').toRate().toFixed(4)  // '0.1235'

// Comparações
Decimal.from('1.23').gt('1.22')  // true
Decimal.max('100', '200')        // Decimal('200')

// Imutável. 7 modos de arredondamento. Encadeável.

Cada centavo rastreável

A SEFAZ rejeitou. E agora?

Com .toFixed(2), você sabe que deu errado. Com tributos-br, você sabe onde deu errado.

Todo calculador retorna um array audit: AuditStep[] com cada operação intermediária. Nome do passo, fórmula aplicada, valor resultante.

A maioria dos ERPs menores não expõe isso. Quando o fiscal liga perguntando "de onde veio esse número", você tem a resposta.

calcSt() → audit ✓ 4 passos
Base ICMS1000.00
ICMS Próprio (12%)120.00
Base ST (MVA + IPI)1540.00
ICMS-ST (1540.00 × 0.18 − 120.00)157.20

Centavos por operação. Milhares por mês.

O erro não parece grande em uma nota. Mas se repete em todas.

R$ 13,17
DIFAL: diferença por operação entre base única e base dupla
R$ 58,83
ST vs antecipação: diferença por operação quando confunde o regime
R$ 29.415
por mês, em 500 operações interestaduais com o método errado

Valores calculados com operação interestadual de R$ 1.000, alíquota inter 12%, interna 18%, MVA 40%.

Por que tributos-br?

O que faz alguém instalar em vez de implementar na mão.

  • 1
    Audit trail em cada cálculo
    Quando a SEFAZ rejeita, você sabe exatamente onde o arredondamento ou a precisão causou a divergência. Cada função retorna um array de steps com fórmula e valor de cada etapa. A maioria das libs e ERPs menores não oferece isso.
  • 2
    DIFAL base dupla (LC 190/2022)
    A maioria dos ERPs menores calcula DIFAL apenas com base única. tributos-br implementa ambos os métodos: base única para não-contribuinte (B2C) e base dupla para contribuinte (B2B), selecionando automaticamente via parâmetro.
  • 3
    FECOP na MVA Ajustada
    ALQ Intra Efetiva = ALQ Interna + FECOP. Ignorar FECOP gera diferença de ~4 pontos percentuais na MVA. Estados com FECOP: RJ, MG, CE, PE, BA, GO, MT, PI.
  • 4
    Função ST unificada
    Uma única calcSt() cobre todos os 5 cenários de Substituição Tributária via parâmetros opcionais: MVA original, MVA ajustada com FECOP, pauta fiscal, redução de base. Sem nomenclatura ST-01 a ST-05.
  • 5
    Não é "só mais uma lib de decimal"
    decimal.js e big.js resolvem precisão, mas você ainda precisa implementar cada fórmula fiscal, cada modo de arredondamento SEFAZ, cada cenário de ST/DIFAL/MVA. tributos-br entrega as calculadoras prontas, tipadas, com audit trail. Zero dependências, incluindo as de decimal.

O que tem dentro

0
dependências
7
calculadores
95%+
cobertura de testes
7
modos de arredondamento
ESM+CJS
dual output
20
dígitos de precisão
Reforma Tributária 2026
A LC 214/2025 cria CBS (substitui PIS/COFINS) e IBS (substitui ICMS/ISS). A partir de 2026, os campos CBS e IBS entram em fase de teste obrigatório na NF-e (NT 2025.002), com alíquotas reduzidas (CBS 0,9% + IBS 0,1%). A transição gradual vai até 2033. tributos-br já implementa calcCbs() e calcIbs() com alíquotas parametrizáveis para acompanhar cada fase.

Comece em 30 segundos

Funciona com npm, yarn e pnpm. ESM e CommonJS. TypeScript strict.

npm install tributos-br
Import Conteúdo
tributos-br Calculadoras (ICMS, IPI, DIFAL, ST, CBS/IBS) + Decimal
tributos-br/precision Apenas Decimal + RoundingMode

Se seu sistema emite NF-e, cada centavo importa.

7 calculadores com precisão decimal arbitrária e audit trail. Zero dependências. Instale e esqueça rejeições 629/630.

GitHub
Copiado!