diff --git a/README.md b/README.md index a2e31b25..52683d7f 100644 --- a/README.md +++ b/README.md @@ -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) ``` diff --git a/docs/reforma_tributaria.md b/docs/reforma_tributaria.md index cae67eb2..197cacdc 100644 --- a/docs/reforma_tributaria.md +++ b/docs/reforma_tributaria.md @@ -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 @@ -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, @@ -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", @@ -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", ) ``` @@ -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) | @@ -257,11 +256,11 @@ O grupo `` e adicionado como filho direto de ``, apos ` ``` -Para CSTs nao tributados (ex: 222 — isencao): +Para CSTs nao tributados (ex: 400 — isencao): ```xml - 222 + 400 000002 ``` @@ -324,13 +323,13 @@ Os totais ficam em um grupo **separado** de ``, 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 `` 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 `` e ``, sem ``. diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index cb463000..9ef29e04 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -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 diff --git a/pynfe/utils/flags.py b/pynfe/utils/flags.py index 0cdcc5e0..ee63c008 100644 --- a/pynfe/utils/flags.py +++ b/pynfe/utils/flags.py @@ -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) diff --git a/tests/test_nfe_serializacao_reforma_tributaria.py b/tests/test_nfe_serializacao_reforma_tributaria.py index ee042b8a..5f12d386 100644 --- a/tests/test_nfe_serializacao_reforma_tributaria.py +++ b/tests/test_nfe_serializacao_reforma_tributaria.py @@ -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) @@ -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) @@ -239,13 +239,13 @@ 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) @@ -253,6 +253,47 @@ def test_cst222_isencao_sem_valores(self): 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 # ------------------------------------------------------------------ @@ -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()