Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ Exemplos de uso
from pynfe.processamento.comunicacao import ComunicacaoSefaz

certificado = "/home/user/certificado.pfx"
senha = 'senha'
uf = 'pr'
senha = "senha"
uf = "pr"
homologacao = True

con = ComunicacaoSefaz(uf, certificado, senha, homologacao)
xml = con.status_servico('nfe')
xml = con.status_servico("nfe")
print(xml.text)
```

Expand Down
69 changes: 34 additions & 35 deletions docs/reforma_tributaria.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,23 @@ from pynfe.utils.flags import IBS_CBS_TIPOS_TRIBUTACAO
| CST | Descricao |
|-----|-----------|
| 000 | Tributacao integral |
| 010 | Tributacao com aliquota reduzida |
| 100 | Tributacao com suspensao |
| 110 | Imunidade — exportacao |
| 200 | Tributacao com diferimento |
| 222 | Isencao |
| 300 | Nao incidencia |
| 400 | Tributacao por substituicao |
| 410 | Tributacao com aliquota zero |
| 510 | Tributacao integral — regime especifico |
| 600 | Tributacao monofasica — incidencia padrao |
| 620 | Tributacao monofasica — demais operacoes |
| 800 | Credito presumido |
| 810 | Credito presumido — ZFM |
| 900 | Outros |
| 010 | Tributacao com aliquotas uniformes |
| 011 | Tributacao com aliquotas uniformes reduzidas |
| 200 | Aliquota reduzida |
| 220 | Aliquota fixa |
| 221 | Aliquota fixa proporcional |
| 222 | Reducao de Base de Calculo |
| 400 | Isencao |
| 410 | Imunidade e nao incidencia |
| 510 | Diferimento |
| 515 | Diferimento com reducao de aliquota |
| 550 | Suspensao |
| 620 | Tributacao Monofasica |
| 800 | Transferencia de credito |
| 810 | Ajuste de IBS na ZFM |
| 811 | Ajustes |
| 820 | Tributacao em documento especifico |
| 830 | Exclusao da Base de Calculo |

### IS (Imposto Seletivo) — CSTIS de 2 digitos

Expand Down Expand Up @@ -107,7 +110,6 @@ nota_fiscal.adicionar_produto_servico(
valor_unitario_tributavel=Decimal("100.00"),
ean_tributavel="SEM GTIN",
ind_total=1,

# ... impostos legados (ICMS, PIS, COFINS) continuam obrigatorios ...
icms_modalidade="00",
icms_origem=0,
Expand All @@ -116,19 +118,17 @@ nota_fiscal.adicionar_produto_servico(
cofins_modalidade="99",
pis_valor=Decimal("0.00"),
cofins_valor=Decimal("0.00"),

# IBSCBS — Group UB
ibscbs_cst="000", # CST 3 digitos
ibscbs_c_class_trib="000001", # cClassTrib 6 digitos
ibscbs_vbc=Decimal("1000.00"), # Base de calculo compartilhada IBS+CBS
ibscbs_p_ibs_uf=Decimal("0.1000"), # Aliquota IBS UF (4 casas)
ibscbs_v_ibs_uf=Decimal("1.00"), # Valor IBS UF
ibscbs_p_ibs_mun=Decimal("0.0000"), # Aliquota IBS Municipal
ibscbs_v_ibs_mun=Decimal("0.00"), # Valor IBS Municipal
ibscbs_v_ibs=Decimal("1.00"), # Valor total IBS (UF + Mun)
ibscbs_p_cbs=Decimal("0.9000"), # Aliquota CBS (4 casas)
ibscbs_v_cbs=Decimal("9.00"), # Valor CBS

ibscbs_cst="000", # CST 3 digitos
ibscbs_c_class_trib="000001", # cClassTrib 6 digitos
ibscbs_vbc=Decimal("1000.00"), # Base de calculo compartilhada IBS+CBS
ibscbs_p_ibs_uf=Decimal("0.1000"), # Aliquota IBS UF (4 casas)
ibscbs_v_ibs_uf=Decimal("1.00"), # Valor IBS UF
ibscbs_p_ibs_mun=Decimal("0.0000"), # Aliquota IBS Municipal
ibscbs_v_ibs_mun=Decimal("0.00"), # Valor IBS Municipal
ibscbs_v_ibs=Decimal("1.00"), # Valor total IBS (UF + Mun)
ibscbs_p_cbs=Decimal("0.9000"), # Aliquota CBS (4 casas)
ibscbs_v_cbs=Decimal("9.00"), # Valor CBS
# IS — armazenado na entidade mas NAO serializado no XML (2026)
# is_cst_selec="01",
# is_c_class_trib="010001",
Expand All @@ -138,14 +138,13 @@ nota_fiscal.adicionar_produto_servico(
)
```

### Exemplo: produto isento (CST 222)
### Exemplo: produto isento (CST 400)

```python
nota_fiscal.adicionar_produto_servico(
# ... campos do produto ...

# IBSCBS isento — apenas CST e cClassTrib, sem valores
ibscbs_cst="222",
ibscbs_cst="400",
ibscbs_c_class_trib="000002",
)
```
Expand Down Expand Up @@ -178,7 +177,7 @@ nota_fiscal = NotaFiscal(

| Campo | Tipo | Descricao |
|-------|------|-----------|
| `ibscbs_cst` | `str` | CST 3 digitos (ex: "000", "222") |
| `ibscbs_cst` | `str` | CST 3 digitos (ex: "000", "400") |
| `ibscbs_c_class_trib` | `str` | cClassTrib 6 digitos (classificacao tributaria) |
| `ibscbs_vbc` | `Decimal` | Base de calculo compartilhada IBS + CBS |
| `ibscbs_p_ibs_uf` | `Decimal` | Aliquota IBS estadual (4 casas decimais) |
Expand Down Expand Up @@ -257,11 +256,11 @@ O grupo `<IBSCBS>` e adicionado como filho direto de `<imposto>`, apos `<COFINS>
</det>
```

Para CSTs nao tributados (ex: 222 — isencao):
Para CSTs nao tributados (ex: 400 — isencao):

```xml
<IBSCBS>
<CST>222</CST>
<CST>400</CST>
<cClassTrib>000002</cClassTrib>
</IBSCBS>
```
Expand Down Expand Up @@ -324,13 +323,13 @@ Os totais ficam em um grupo **separado** de `<ICMSTot>`, como irmao dentro de `<

### CSTs tributados (emitem `gIBSCBS` com valores)

CSTs: 000, 010, 200, 400, 510, 600, 620, 800, 810, 900
CSTs: 000, 010, 011, 200, 220, 221, 222, 510, 515, 550, 830

Esses CSTs geram o subgrupo `<gIBSCBS>` completo com `vBC`, `gIBSUF`, `gIBSMun`, `vIBS` e `gCBS`.

### CSTs nao tributados (apenas CST + cClassTrib)

CSTs: 100, 110, 222, 300, 410
CSTs: 400, 410, 620, 800, 810, 811, 820

Esses CSTs geram apenas `<CST>` e `<cClassTrib>`, sem `<gIBSCBS>`.

Expand Down
16 changes: 14 additions & 2 deletions pynfe/processamento/serializacao.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,8 +1316,20 @@ def _serializar_imposto_importacao(
# Reforma Tributaria - IVA Dual (NT 2025.002-RTC)
# =============================================

# CSTs that have taxable values (vBC, rates, amounts)
_IBSCBS_CST_TRIBUTADOS = ("000", "010", "200", "400", "510", "600", "620", "800", "810", "900")
# CSTs que possuem valores tributáveis (vBC, alíquotas, valores) — IndExigeTrib=True na NT 2025.002-RTC
_IBSCBS_CST_TRIBUTADOS = (
"000",
"010",
"011",
"200",
"220",
"221",
"222",
"510",
"515",
"550",
"830",
)

def _serializar_imposto_ibscbs(
self, produto_servico, modelo, tag_raiz="imposto", retorna_string=True
Expand Down
31 changes: 17 additions & 14 deletions pynfe/utils/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,20 +554,23 @@
# CST para IBSCBS (IBS + CBS) — NT 2025.002-RTC (3-digit codes)
IBS_CBS_TIPOS_TRIBUTACAO = (
("000", "Tributação integral"),
("010", "Tributação com alíquota reduzida"),
("100", "Tributação com suspensão"),
("110", "Imunidade — exportação"),
("200", "Tributação com diferimento"),
("222", "Isenção"),
("300", "Não incidência"),
("400", "Tributação por substituição"),
("410", "Tributação com alíquota zero"),
("510", "Tributação integral — regime específico"),
("600", "Tributação monofásica — incidência padrão"),
("620", "Tributação monofásica — demais operações"),
("800", "Crédito presumido"),
("810", "Crédito presumido — ZFM"),
("900", "Outros"),
("010", "Tributação com alíquotas uniformes"),
("011", "Tributação com alíquotas uniformes reduzidas"),
("200", "Alíquota reduzida"),
("220", "Alíquota fixa"),
("221", "Alíquota fixa proporcional"),
("222", "Redução de Base de Cálculo"),
("400", "Isenção"),
("410", "Imunidade e não incidência"),
("510", "Diferimento"),
("515", "Diferimento com redução de alíquota"),
("550", "Suspensão"),
("620", "Tributação Monofásica"),
("800", "Transferência de crédito"),
("810", "Ajuste de IBS na ZFM"),
("811", "Ajustes"),
("820", "Tributação em documento específico"),
("830", "Exclusão da Base de Cálculo"),
)

# CST para IS (Imposto Seletivo) — CSTSelec (NT 2025.002-RTC)
Expand Down
93 changes: 88 additions & 5 deletions tests/test_nfe_serializacao_reforma_tributaria.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ def test_cst000_ibscbs_tributacao_integral(self):
self.assertEqual(len(is_tag), 0)

# ------------------------------------------------------------------
# Test 2: CST 222 — isenção (no vBC, values zero)
# Test 2: CST 400 — isenção (no vBC, values zero)
# ------------------------------------------------------------------
def test_cst222_isencao_sem_valores(self):
def test_cst400_isencao_sem_valores(self):
emitente = self._emitente()
cliente = self._cliente()
nf = self._nota_fiscal(emitente, cliente)
Expand All @@ -225,7 +225,7 @@ def test_cst222_isencao_sem_valores(self):
valor_total_bruto=Decimal("50.00"),
quantidade_tributavel=Decimal("1"),
valor_unitario_tributavel=Decimal("50.00"),
ibscbs_cst="222",
ibscbs_cst="400",
ibscbs_c_class_trib="000002",
)
nf.adicionar_produto_servico(**kwargs)
Expand All @@ -239,20 +239,61 @@ def test_cst222_isencao_sem_valores(self):

# CST present
cst = xml.xpath("//ns:IBSCBS/ns:CST", namespaces=self.ns)[0].text
self.assertEqual(cst, "222")
self.assertEqual(cst, "400")

# cClassTrib present
cclass = xml.xpath("//ns:IBSCBS/ns:cClassTrib", namespaces=self.ns)[0].text
self.assertEqual(cclass, "000002")

# No gIBSCBS group (CST 222 is not in taxable CSTs)
# No gIBSCBS group (CST 400 is not in taxable CSTs)
gibscbs = xml.xpath("//ns:IBSCBS/ns:gIBSCBS", namespaces=self.ns)
self.assertEqual(len(gibscbs), 0)

# No IS group
is_group = xml.xpath("//ns:det/ns:imposto/ns:IS", namespaces=self.ns)
self.assertEqual(len(is_group), 0)

# ------------------------------------------------------------------
# Test 2b: CST 222 — Redução de Base de Cálculo (IndExigeTrib=True)
# ------------------------------------------------------------------
def test_cst222_reducao_base_calculo_tributado(self):
emitente = self._emitente()
cliente = self._cliente()
nf = self._nota_fiscal(emitente, cliente)

kwargs = self._base_product_kwargs()
kwargs.update(
codigo="002",
descricao="Produto reducao bc reforma tributaria",
ibscbs_cst="222",
ibscbs_c_class_trib="000003",
ibscbs_vbc=Decimal("500.00"),
ibscbs_p_ibs_uf=Decimal("0.1000"),
ibscbs_v_ibs_uf=Decimal("0.50"),
ibscbs_p_ibs_mun=Decimal("0.0000"),
ibscbs_v_ibs_mun=Decimal("0.00"),
ibscbs_v_ibs=Decimal("0.50"),
ibscbs_p_cbs=Decimal("0.9000"),
ibscbs_v_cbs=Decimal("4.50"),
)
nf.adicionar_produto_servico(**kwargs)
nf.adicionar_pagamento(t_pag="01", x_pag="Dinheiro", v_pag=1000.00, ind_pag=0)

xml = self._serializar_e_assinar()

# IBSCBS present
ibscbs = xml.xpath("//ns:det/ns:imposto/ns:IBSCBS", namespaces=self.ns)
self.assertEqual(len(ibscbs), 1)

cst = xml.xpath("//ns:IBSCBS/ns:CST", namespaces=self.ns)[0].text
self.assertEqual(cst, "222")

# gIBSCBS group must be present for CST 222
gibscbs = xml.xpath("//ns:IBSCBS/ns:gIBSCBS", namespaces=self.ns)
self.assertEqual(len(gibscbs), 1)
vbc = xml.xpath("//ns:IBSCBS/ns:gIBSCBS/ns:vBC", namespaces=self.ns)[0].text
self.assertEqual(vbc, "500.00")

# ------------------------------------------------------------------
# Test 3: No reforma data — IBSCBS not emitted
# ------------------------------------------------------------------
Expand Down Expand Up @@ -686,6 +727,48 @@ def test_cmunfgibs_nao_emitido_quando_vazio(self):
cmunfgibs = xml.xpath("//ns:ide/ns:cMunFGIBS", namespaces=self.ns)
self.assertEqual(len(cmunfgibs), 0)

# ------------------------------------------------------------------
# Test 11: Validacao da lista _IBSCBS_CST_TRIBUTADOS e flags NT 2025.002-RTC
# ------------------------------------------------------------------
def test_ibscbs_cst_tributados_e_flags(self):
from pynfe.processamento.serializacao import SerializacaoXML
from pynfe.utils.flags import IBS_CBS_TIPOS_TRIBUTACAO

esperados_tributados = (
"000",
"010",
"011",
"200",
"220",
"221",
"222",
"510",
"515",
"550",
"830",
)
self.assertEqual(SerializacaoXML._IBSCBS_CST_TRIBUTADOS, esperados_tributados)

csts_flags = dict(IBS_CBS_TIPOS_TRIBUTACAO)
self.assertEqual(csts_flags["000"], "Tributação integral")
self.assertEqual(csts_flags["010"], "Tributação com alíquotas uniformes")
self.assertEqual(csts_flags["011"], "Tributação com alíquotas uniformes reduzidas")
self.assertEqual(csts_flags["200"], "Alíquota reduzida")
self.assertEqual(csts_flags["220"], "Alíquota fixa")
self.assertEqual(csts_flags["221"], "Alíquota fixa proporcional")
self.assertEqual(csts_flags["222"], "Redução de Base de Cálculo")
self.assertEqual(csts_flags["400"], "Isenção")
self.assertEqual(csts_flags["410"], "Imunidade e não incidência")
self.assertEqual(csts_flags["510"], "Diferimento")
self.assertEqual(csts_flags["515"], "Diferimento com redução de alíquota")
self.assertEqual(csts_flags["550"], "Suspensão")
self.assertEqual(csts_flags["620"], "Tributação Monofásica")
self.assertEqual(csts_flags["800"], "Transferência de crédito")
self.assertEqual(csts_flags["810"], "Ajuste de IBS na ZFM")
self.assertEqual(csts_flags["811"], "Ajustes")
self.assertEqual(csts_flags["820"], "Tributação em documento específico")
self.assertEqual(csts_flags["830"], "Exclusão da Base de Cálculo")


if __name__ == "__main__":
unittest.main()
Loading