From e81e28b5f191c2a03aefc97933b9f7d4b6dfab5d Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:30:42 -0300 Subject: [PATCH 001/175] feat(cte-event): Adiciona arquivos XSDs para cte --- pynfe/data/XSDs/CT-e/GTVe_v4.00.xsd | 10 + .../XSDs/CT-e/consSitCTeTiposBasico_v4.00.xsd | 123 + pynfe/data/XSDs/CT-e/consSitCTe_v4.00.xsd | 10 + .../data/XSDs/CT-e/consStatServCTe_v4.00.xsd | 10 + .../CT-e/consStatServTiposBasico_v4.00.xsd | 115 + pynfe/data/XSDs/CT-e/cteModalAereo_v4.00.xsd | 240 + .../XSDs/CT-e/cteModalAquaviario_v4.00.xsd | 207 + .../XSDs/CT-e/cteModalDutoviario_v4.00.xsd | 30 + .../XSDs/CT-e/cteModalFerroviario_v4.00.xsd | 231 + .../XSDs/CT-e/cteModalRodoviarioOS_v4.00.xsd | 193 + .../XSDs/CT-e/cteModalRodoviario_v4.00.xsd | 102 + pynfe/data/XSDs/CT-e/cteMultiModal_v4.00.xsd | 100 + pynfe/data/XSDs/CT-e/cteOS_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/cteSimp_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/cteTiposBasico_v4.00.xsd | 7966 +++++++++++++++++ pynfe/data/XSDs/CT-e/cte_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/evCCeCTe_v4.00.xsd | 98 + pynfe/data/XSDs/CT-e/evCECTe_v4.00.xsd | 108 + pynfe/data/XSDs/CT-e/evCancCECTe_v4.00.xsd | 36 + pynfe/data/XSDs/CT-e/evCancCTe_v4.00.xsd | 36 + pynfe/data/XSDs/CT-e/evCancIECTe_v4.00.xsd | 36 + .../XSDs/CT-e/evCancPrestDesacordo_v4.00.xsd | 32 + pynfe/data/XSDs/CT-e/evEPECCTe_v4.00.xsd | 161 + pynfe/data/XSDs/CT-e/evGTV_v4.00.xsd | 255 + pynfe/data/XSDs/CT-e/evIECTe_v4.00.xsd | 126 + .../data/XSDs/CT-e/evPrestDesacordo_v4.00.xsd | 49 + .../data/XSDs/CT-e/evRegMultimodal_v4.00.xsd | 51 + .../XSDs/CT-e/eventoCTeTiposBasico_v4.00.xsd | 331 + pynfe/data/XSDs/CT-e/eventoCTe_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/procCTeOS_v4.00.xsd | 37 + pynfe/data/XSDs/CT-e/procCTeSimp_v4.00.xsd | 37 + pynfe/data/XSDs/CT-e/procCTe_v4.00.xsd | 37 + pynfe/data/XSDs/CT-e/procEventoCTe_v4.00.xsd | 15 + pynfe/data/XSDs/CT-e/procGTVe_v4.00.xsd | 37 + pynfe/data/XSDs/CT-e/retCTeOS_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/retCTeSimp_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/retCTe_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/retConsSitCTe_v4.00.xsd | 10 + .../XSDs/CT-e/retConsStatServCTe_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/retEventoCTe_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/retGTVe_v4.00.xsd | 10 + pynfe/data/XSDs/CT-e/tiposGeralCTe_v4.00.xsd | 647 ++ .../XSDs/CT-e/xmldsig-core-schema_v1.01.xsd | 98 + 43 files changed, 11674 insertions(+) create mode 100644 pynfe/data/XSDs/CT-e/GTVe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/consSitCTeTiposBasico_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/consSitCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/consStatServCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/consStatServTiposBasico_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalAereo_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalAquaviario_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalDutoviario_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalFerroviario_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalRodoviarioOS_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteModalRodoviario_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteMultiModal_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteOS_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteSimp_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cteTiposBasico_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/cte_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCCeCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCECTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCancCECTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCancCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCancIECTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evCancPrestDesacordo_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evEPECCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evGTV_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evIECTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evPrestDesacordo_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/evRegMultimodal_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/eventoCTeTiposBasico_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/eventoCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/procCTeOS_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/procCTeSimp_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/procCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/procEventoCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/procGTVe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retCTeOS_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retCTeSimp_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retConsSitCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retConsStatServCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retEventoCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/retGTVe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/tiposGeralCTe_v4.00.xsd create mode 100644 pynfe/data/XSDs/CT-e/xmldsig-core-schema_v1.01.xsd diff --git a/pynfe/data/XSDs/CT-e/GTVe_v4.00.xsd b/pynfe/data/XSDs/CT-e/GTVe_v4.00.xsd new file mode 100644 index 00000000..66cd2adb --- /dev/null +++ b/pynfe/data/XSDs/CT-e/GTVe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Guia de Trasnsporte Eletrônica + + + diff --git a/pynfe/data/XSDs/CT-e/consSitCTeTiposBasico_v4.00.xsd b/pynfe/data/XSDs/CT-e/consSitCTeTiposBasico_v4.00.xsd new file mode 100644 index 00000000..27525a23 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/consSitCTeTiposBasico_v4.00.xsd @@ -0,0 +1,123 @@ + + + + + + + + Tipo Pedido de Consulta da Situação Atual do Conhecimento de Transporte eletrônico + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Serviço Solicitado + + + + + Chaves de acesso da CT-e + + + + + + + + + + + + Tipo Retorno de Pedido de Consulta da Situação Atual do Conhecimento de Transporte eletrônico + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que processou o CT-e + + + + + Código do status da mensagem enviada. + + + + + Descrição literal do status do serviço solicitado. + + + + + código da UF de atendimento + + + + + + + + Retornar protCTe da versão correspondente do CT-e autorizado + + + + + + + + + + + + + + + + + + + + + + + Retornar procEventoCTe da versão correspondente do evento CT-e autorizado + + + + + + + + + + + + + + + + + + + + + + Tipo Versão do Consulta situação de CT-e - 4.00 + + + + + + diff --git a/pynfe/data/XSDs/CT-e/consSitCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/consSitCTe_v4.00.xsd new file mode 100644 index 00000000..c9825f32 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/consSitCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema de validação XML dp Pedido de Consulta da Situação Atual do CT-e. + + + diff --git a/pynfe/data/XSDs/CT-e/consStatServCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/consStatServCTe_v4.00.xsd new file mode 100644 index 00000000..bc2ed359 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/consStatServCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do Pedido de Consulta do Status do Serviço CT-e + + + diff --git a/pynfe/data/XSDs/CT-e/consStatServTiposBasico_v4.00.xsd b/pynfe/data/XSDs/CT-e/consStatServTiposBasico_v4.00.xsd new file mode 100644 index 00000000..a79fae2c --- /dev/null +++ b/pynfe/data/XSDs/CT-e/consStatServTiposBasico_v4.00.xsd @@ -0,0 +1,115 @@ + + + + + + + Tipo Pedido de Consulta do Status do Serviço CTe + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Código da UF a ser verificado o status + Utilizar a Tabela do IBGE. + + + + + Serviço Solicitado + + + + + + + + + + + + Tipo Resultado da Consulta do Status do Serviço CTe + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que processou o CT-e + + + + + + + + + + Código do status da mensagem enviada. + + + + + Descrição literal do status do serviço solicitado. + + + + + Código da UF responsável pelo serviço + + + + + AAAA-MM-DDTHH:MM:SS TZD + + + + + Tempo médio de resposta do serviço (em segundos) dos últimos 5 minutos + + + + + + + + + + AAAA-MM-DDTHH:MM:SSDeve ser preenchida com data e hora previstas para o retorno dos serviços prestados. + + + + + Campo observação utilizado para incluir informações ao contribuinte + + + + + + + + + + + + + + + Tipo Versão do Consulta do Status do Serviço CTe + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalAereo_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalAereo_v4.00.xsd new file mode 100644 index 00000000..fbb25460 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalAereo_v4.00.xsd @@ -0,0 +1,240 @@ + + + + + + + Informações do modal Aéreo + + + + + + Número da Minuta + Documento que precede o CT-e, assinado pelo expedidor, espécie de pedido de serviço + + + + + + + + + + + Número Operacional do Conhecimento Aéreo + Representa o número de controle comumente utilizado pelo conhecimento aéreo composto por uma sequência numérica de onze dígitos. Os três primeiros dígitos representam um código que os operadores de transporte aéreo associados à IATA possuem. Em seguida um número de série de sete dígitos determinados pelo operador de transporte aéreo. Para finalizar, um dígito verificador, que é um sistema de módulo sete imponderado o qual divide o número de série do conhecimento aéreo por sete e usa o resto como dígito de verificação. + + + + + + + + + + + Data prevista da entrega + Formato AAAA-MM-DD + + + + + Natureza da carga + + + + + + Dimensão + Formato:1234X1234X1234 (cm). Esse campo deve sempre que possível ser preenchido. Entretanto, quando for impossível o preenchimento das dimensões, fica obrigatório o preenchimento da cubagem em metro cúbico do leiaute do CT-e da estrutura genérica (infQ). + + + + + + + + + + + + Informações de manuseio + 01 - certificado do expedidor para embarque de animal vivo; + +02 - artigo perigoso conforme Declaração do Expedidor anexa; + +03 - somente em aeronave cargueira; + +04 - artigo perigoso - declaração do expedidor não requerida; + +05 - artigo perigoso em quantidade isenta; + +06 - gelo seco para refrigeração (especificar no campo observações a quantidade); + +07 - não restrito (especificar a Disposição Especial no campo observações); + +08 - artigo perigoso em carga consolidada (especificar a quantidade no campo observações) +; +09 - autorização da autoridade governamental anexa (especificar no campo observações); + +10 – baterias de íons de lítio em conformidade com a Seção II da PI965 – CAO +; +11 - baterias de íons de lítio em conformidade com a Seção II da PI966 +; +12 - baterias de íons de lítio em conformidade com a Seção II da PI967 +; +13 – baterias de metal lítio em conformidade com a Seção II da PI968 — CAO; + +14 - baterias de metal lítio em conformidade com a Seção II da PI969; + +15 - baterias de metal lítio em conformidade com a Seção II da PI970 +; +99 - outro (especificar no campo observações) +. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Informações de tarifa + + + + + + Classe + Preencher com: + M - Tarifa Mínima; + G - Tarifa Geral; + E - Tarifa Específica + + + + + + + + + + + + + + Código da Tarifa + Deverão ser incluídos os códigos de três dígitos, correspondentes à tarifa. + + + + + + + + + + + Valor da Tarifa + Valor da tarifa por kg quando for o caso. + + + + + + + + Preenchido quando for transporte de produtos classificados pela ONU como perigosos. + O preenchimento desses campos não desobriga a empresa aérea de emitir os demais documentos que constam na legislação vigente. + + + + + + Número ONU/UN + Ver a legislação de transporte de produtos perigosos aplicadas ao modal + + + + + + + + + + + Quantidade total de volumes contendo artigos perigosos + Preencher com o número de volumes (unidades) de artigos perigosos, ou seja, cada embalagem devidamente marcada e etiquetada (por ex.: número de caixas, de tambores, de bombonas, dentre outros). Não deve ser preenchido com o número de ULD, pallets ou containers. + + + + + + + + + + + Grupo de informações das quantidades totais de artigos perigosos + Preencher conforme a legislação de transporte de produtos perigosos aplicada ao modal + + + + + + Quantidade total de artigos perigosos + 15 posições, sendo 11 inteiras e 4 decimais. +Deve indicar a quantidade total do artigo perigoso, tendo como base a unidade referenciada na Tabela 3-1 do Doc 9284, por exemplo: litros; quilogramas; quilograma bruto etc. O preenchimento não deve, entretanto, incluir a unidade de medida. No caso de transporte de material radioativo, deve-se indicar o somatório dos Índices de Transporte (TI). Não indicar a quantidade do artigo perigoso por embalagem. + + + + + Unidade de medida + 1 – KG; +2 – KG G (quilograma bruto); +3 – LITROS; +4 – TI (índice de transporte para radioativos); 5- Unidades (apenas para artigos perigosos medidos em unidades que não se enquadram nos itens acima. Exemplo: baterias, celulares, equipamentos, veículos, dentre outros) + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalAquaviario_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalAquaviario_v4.00.xsd new file mode 100644 index 00000000..a4fe0839 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalAquaviario_v4.00.xsd @@ -0,0 +1,207 @@ + + + + + + + Informações do modal Aquaviário + + + + + + Valor da Prestação Base de Cálculo do AFRMM + + + + + AFRMM (Adicional de Frete para Renovação da Marinha Mercante) + + + + + Identificação do Navio + + + + + + + + + + + Grupo de informações das balsas + + + + + + Identificador da Balsa + + + + + + + + + + + + + + Número da Viagem + + + + + + + + + + + Direção + Preencher com: N-Norte, L-Leste, S-Sul, O-Oeste + + + + + + + + + + + + + + Irin do navio sempre deverá ser informado + + + + + + + + + + + + Grupo de informações de detalhamento dos conteiners +(Somente para Redespacho Intermediário e Serviço Vinculado a Multimodal) + + + + + + Identificação do Container + + + + + Grupo de informações dos lacres dos cointainers da qtde da carga + + + + + + Lacre + + + + + + + + + + + + + + Informações dos documentos dos conteiners + + + + + + Informações das NF + + + + + + Série + + + + + + + + + + + Número + + + + + + + + + + + Unidade de medida rateada (Peso,Volume) + + + + + + + + Informações das NFe + + + + + + Chave de acesso da NF-e + + + + + Unidade de medida rateada (Peso,Volume) + + + + + + + + + + + + + + Tipo de Navegação + Preencher com: + 0 - Interior; + 1 - Cabotagem + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalDutoviario_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalDutoviario_v4.00.xsd new file mode 100644 index 00000000..bc9c8940 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalDutoviario_v4.00.xsd @@ -0,0 +1,30 @@ + + + + + + + + Informações do modal Dutoviário + + + + + + Valor da tarifa + + + + + Data de Início da prestação do serviço + + + + + Data de Fim da prestação do serviço + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalFerroviario_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalFerroviario_v4.00.xsd new file mode 100644 index 00000000..2a789ac6 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalFerroviario_v4.00.xsd @@ -0,0 +1,231 @@ + + + + + + + + Tipo Dados do Endereço + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município + Utilizar a tabela do IBGE + Informar 9999999 para operações com o exterior. + + + + + Nome do município + Informar EXTERIOR para operações com o exterior. + + + + + + + + + + + CEP + + + + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + + + Informações do modal Ferroviário + + + + + + Tipo de Tráfego + Preencher com: + 0-Próprio; + 1-Mútuo; + 2-Rodoferroviário; + 3-Rodoviário. + + + + + + + + + + + + + + Detalhamento de informações para o tráfego mútuo + + + + + + Responsável pelo Faturamento + Preencher com: + 1-Ferrovia de origem; + 2-Ferrovia de destino + + + + + + + + + + + + Ferrovia Emitente do CTe + Preencher com: + 1-Ferrovia de origem; + 2-Ferrovia de destino + + + + + + + + + + + + Valor do Frete do Tráfego Mútuo + + + + + Chave de acesso do CT-e emitido pelo ferrovia de origem + + + + + Informações das Ferrovias Envolvidas + + + + + + Número do CNPJ + Informar o CNPJ da Ferrovia Envolvida. Caso a Ferrovia envolvida não seja inscrita no CNPJ o campo deverá preenchido com zeros. +Informar os zeros não significativos. + + + + + Código interno da Ferrovia envolvida + Uso da transportadora + + + + + + + + + + + Inscrição Estadual + + + + + Razão Social ou Nome + + + + + + + + + + + Dados do endereço da ferrovia envolvida + + + + + + + + + + + Fluxo Ferroviário + Trata-se de um número identificador do contrato firmado com o cliente + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalRodoviarioOS_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalRodoviarioOS_v4.00.xsd new file mode 100644 index 00000000..23bcb62a --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalRodoviarioOS_v4.00.xsd @@ -0,0 +1,193 @@ + + + + + + + + Tipo Termo de Autorização de Fretamento – TAF + + + + + + + + + + Tipo Número de Registro Estadual + + + + + + + + + + Informações do modal Rodoviário + + + + + + + Termo de Autorização de Fretamento – TAF + Registro obrigatório do emitente do CT-e OS junto à ANTT, de acordo com a Resolução ANTT nº 4.777/2015 + + + + + Número do Registro Estadual + Registro obrigatório do emitente do CT-e OS junto à Agência Reguladora Estadual. + + + + + + Dados do Veículo + + + + + + Placa do veículo + + + + + + + + RENAVAM do veículo + + + + + + + + + + + Proprietário ou possuidor do Veículo. +Só preenchido quando o veículo não pertencer à empresa emitente do CT-e OS + + + + + + + Número do CPF + Informar os zeros não significativos. + + + + + Número do CNPJ + Informar os zeros não significativos. + + + + + + + Termo de Autorização de Fretamento – TAF + De acordo com a Resolução ANTT nº 4.777/2015 + + + + + Número do Registro Estadual + Registro obrigatório do emitente do CT-e OS junto à Agência Reguladora Estadual + + + + + + Razão Social ou Nome do proprietário + + + + + + + + + + + + Inscrição Estadual + + + + + + + + UF + + + + + + Tipo Proprietário ou possuidor + Preencher com: + 0-TAC – Agregado; + 1-TAC Independente; ou + 2 – Outros. + + + + + + + + + + + + + + + + UF em que veículo está licenciado + Sigla da UF de licenciamento do veículo. + + + + + + + + Dados do fretamento (apenas para Transporte de Pessoas) + + + + + + Tipo Fretamento + Preencher com: + 1 - Eventual 2 - Continuo + + + + + + + + + + + + Data e hora da viagem (Apenas para fretamento eventual) + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteModalRodoviario_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteModalRodoviario_v4.00.xsd new file mode 100644 index 00000000..21023229 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteModalRodoviario_v4.00.xsd @@ -0,0 +1,102 @@ + + + + + + + + Informações do modal Rodoviário + + + + + + Registro Nacional de Transportadores Rodoviários de Carga + Registro obrigatório do emitente do CT-e junto à ANTT para exercer a atividade de transportador rodoviário de cargas por conta de terceiros e mediante remuneração. + + + + + + + + + Ordens de Coleta associados + + + + + + Série da OCC + + + + + + + + + + + Número da Ordem de coleta + + + + + + + + + + + Data de emissão da ordem de coleta + Formato AAAA-MM-DD + + + + + + + + Número do CNPJ + Informar os zeros não significativos. + + + + + Código interno de uso da transportadora + Uso intermo das transportadoras. + + + + + + + + + + + Inscrição Estadual + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + Telefone + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteMultiModal_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteMultiModal_v4.00.xsd new file mode 100644 index 00000000..05dc7949 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteMultiModal_v4.00.xsd @@ -0,0 +1,100 @@ + + + + + + + + Informações do Multimodal + + + + + + Número do Certificado do Operador de Transporte Multimodal + + + + + + + + + + + + + Indicador Negociável +Preencher com: 0 - Não Negociável; 1 - Negociável + + + + + + + + + + + + Informações de Seguro do Multimodal + + + + + + Informações da seguradora + + + + + + Nome da Seguradora + + + + + + + + + + + Número do CNPJ da seguradora + Obrigatório apenas se responsável pelo seguro for (2) responsável pela contratação do transporte - pessoa jurídica + + + + + + + + Número da Apólice + Obrigatório pela lei 11.442/07 (RCTRC) + + + + + + + + + + + Número da Averbação + Não é obrigatório, pois muitas averbações ocorrem aapós a emissão do CT, mensalmente, por exemplo. + + + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cteOS_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteOS_v4.00.xsd new file mode 100644 index 00000000..9ac66b26 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteOS_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Conhecimento de Transporte Eletrônico Outros Serviços + + + diff --git a/pynfe/data/XSDs/CT-e/cteSimp_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteSimp_v4.00.xsd new file mode 100644 index 00000000..a6b23e03 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteSimp_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Conhecimento de Transporte Eletrônico Simplificado + + + diff --git a/pynfe/data/XSDs/CT-e/cteTiposBasico_v4.00.xsd b/pynfe/data/XSDs/CT-e/cteTiposBasico_v4.00.xsd new file mode 100644 index 00000000..54889a58 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cteTiposBasico_v4.00.xsd @@ -0,0 +1,7966 @@ + + + + + + + + Tipo Modal transporte GTVe + + + + + + + + + + Tipo Finalidade da GTV-e + + + + + + + + + Tipo Guia de Transporte de Valores Eletrônica (Modelo 64) + + + + + Informações do CT-e do tipo GTV-e + + + + + + Identificação da GTV-e + + + + + + Código da UF do emitente da GTV-e. + Utilizar a Tabela do IBGE. + + + + + Código numérico que compõe a Chave de Acesso. + Número aleatório gerado pelo emitente para cada CT-e, com o objetivo de evitar acessos indevidos ao documento. + + + + + + + + + + + Código Fiscal de Operações e Prestações + + + + + Natureza da Operação + + + + + + + + + + + Modelo do documento fiscal + Utilizar o código 64 para identificação do CT-e Guia de Transporte de Valores + + + + + Série da GTV-e + Preencher com "0" no caso de série única + + + + + + + + Número da GTV-e + + + + + Data e hora de emissão da GTV-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Formato de impressão do DACTE + Preencher com: 1 - Retrato; 2 - Paisagem. + + + + + + + + + + + + Forma de emissão da GTV-e + Preencher com: +1 - Normal; + 2- Contingencia offline +7 - Autorização pela SVC-RS; + 8 - Autorização pela SVC-SP + + + + + + + + + + + + + + Digito Verificador da chave de acesso da GTV-e + Informar o dígito de controle da chave de acesso do CT-e, que deve ser calculado com a aplicação do algoritmo módulo 11 (base 2,9) da chave de acesso. + + + + + + + + + + + Tipo do Ambiente + Preencher com:1 - Produção; 2 - Homologação + + + + + Tipo da GTV-e + Preencher com: + 4 - GTV-e + + + + + + + + Versão do processo de emissão + Iinformar a versão do aplicativo emissor de CT-e. + + + + + + + + + + + Código do Município de envio da GTV-e (de onde o documento foi transmitido) + Utilizar a tabela do IBGE. Informar 9999999 para as operações com o exterior. + + + + + Nome do Município de envio da GTV-e (de onde o documento foi transmitido) + Informar PAIS/Municipio para as operações com o exterior. + + + + + + + + + + + Sigla da UF de envio da GTV-e (de onde o documento foi transmitido) + Informar 'EX' para operações com o exterior. + + + + + Modal da GTV-e + Preencher com: +01-Rodoviário +06-Multimodal + + + + + + + + + + + Tipo do Serviço + Preencher com: + +9 - GTV + + + + + + + + + + + Indicador da IE do tomador: +1 – Contribuinte ICMS; +2 – Contribuinte isento de inscrição; +9 – Não Contribuinte + Aplica-se ao tomador que for indicado no toma3 ou toma4 + + + + + + + + + + + + + Data e hora de saida da origem + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Data e hora de chegada no destino + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + + Indicador do "papel" do tomador do serviço no GT-e + + + + + + Tomador do Serviço + Preencher com: + 0-Remetente; + 1-Destinatário + + + + + + + + + + + + + + + + Indicador do "papel" do tomador do serviço no CTV-e + + + + + + Tomador do Serviço + Preencher com: + 4 - Outros + Obs: Informar os dados cadastrais do tomador do serviço + + + + + + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. +Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do tomador ou ISENTO se tomador é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o tomador não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão Social ou Nome + + + + + + + + + + + Nome Fantasia + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + + Informar apenas +para tpEmis diferente de 1 + + + + Data e Hora da entrada em contingência + Informar a data e hora no formato AAAA-MM-DDTHH:MM:SS + + + + + Justificativa da entrada em contingência + + + + + + + + + + + + + + + Dados complementares da GTV-e para fins operacionais ou comerciais + + + + + + Característica adicional do transporte + Texto livre: +REENTREGA; DEVOLUÇÃO; REFATURAMENTO; etc + + + + + + + + + + + Característica adicional do serviço + Texto livre: + ENTREGA EXPRESSA; LOGÍSTICA REVERSA; CONVENCIONAL; EMERGENCIAL; etc + + + + + + + + + + + Funcionário emissor da GTV-e + + + + + + + + + + + Observações Gerais + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + + + + Identificação do Emitente da GTV-e + + + + + + CNPJ do emitente + Informar zeros não significativos + + + + + Inscrição Estadual do Emitente + + + + + + + + Inscrição Estadual do Substituto Tributário + + + + + + + + Razão social ou Nome do emitente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Endereço do emitente + + + + + + + + Informações do Remetente + Poderá não ser informado para os CT-e de redespacho intermediário e serviço vinculado a multimodal. Nos demais casos deverá sempre ser informado. + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do remetente ou ISENTO se remetente é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o remetente não seja contribuinte do ICMS não informar a tag. + + + + + + + + Razão social ou nome do remetente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + + + + Informações do Destinatário + Poderá não ser informado para os CT-e de redespacho intermediário e serviço vinculado a multimodal. Nos demais casos deverá sempre ser informado. + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do destinatário ou ISENTO se destinatário é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o destinatário não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão Social ou Nome do destinatário + + + + + + + + + + + Telefone + + + + + Inscrição na SUFRAMA + (Obrigatório nas operações com as áreas com benefícios de incentivos fiscais sob controle da SUFRAMA) + + + + + + + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + Informações do endereço da origem do serviço + + + + + Informações do endereço do destino do serviço + + + + + Grupo de informações detalhadas da GTV-e + + + + + + Informações das Espécies transportadas + + + + + + Tipo da Espécie + 1 - Cédula +2 - Cheque +3 - Moeda +4 - Outros + + + + + + + + + + + + + + Valor Transportada em Espécie indicada + + + + + Nacionalidade do Numerário + 1 - Nacional +2 - Estrangeiro + + + + + + + + + + + + Nome da Moeda + Informar somente se tipo de numerário for 2 - Estrangeiro + + + + + + + + + + + + + + Quantidade de volumes/malotes + + + + + Grupo de informações dos veículos utilizados no transporte de valores + + + + + + Placa do veículo + + + + + UF em que veículo está licenciado + Sigla da UF de licenciamento do veículo. + + + + + RNTRC do transportador + + + + + + + + + + + + + + + + + Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos. + + + + + + + CNPJ do autorizado + Informar zeros não significativos + + + + + CPF do autorizado + Informar zeros não significativos + + + + + + + + + Informações do Responsável Técnico pela emissão do DF-e + + + + + + Versão do leiaute + Ex: "4.00" + + + + + + + + Identificador da tag a ser assinada + Informar a chave de acesso do CT-e OS e precedida do literal "CTe" + + + + + + + + + + + + Informações suplementares da GTV-e + + + + + + Texto com o QR-Code impresso no DACTE + + + + + + + + + + + + + + + + + + Versão do leiaute + + + + + + + + + Tipo Protocolo de status resultado do processamento da CT-e + + + + + Dados do protocolo de status + + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que processou o CT-e + + + + + Chaves de acesso da CT-e, + + + + + Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD. + + + + + Número do Protocolo de Status do CT-e. + + + + + Digest Value da CT-e processado. Utilizado para conferir a integridade do CT-e original. + + + + + Código do status do CT-e. + + + + + + + + Descrição literal do status do CT-e. + + + + + + + + + Mensagem do Fisco + + + + + + Código do status da mensagem do fisco + + + + + + + + Mensagem do Fisco + + + + + + + + + + + + + + + + Tipo Protocolo de status resultado do processamento do CT-e OS (Modelo 67) + + + + + Dados do protocolo de status + + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que processou o CT-e + + + + + Chaves de acesso da CT-e + + + + + Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD. + + + + + Número do Protocolo de Status do CT-e. + + + + + Digest Value da CT-e processado. Utilizado para conferir a integridade do CT-e original. + + + + + Código do status do CT-e. + + + + + + + + Descrição literal do status do CT-e. + + + + + + + + + Mensagem do Fisco + + + + + + Código do status da mensagem do fisco + + + + + + + + Mensagem do Fisco + + + + + + + + + + + + + + + + Tipo Protocolo de status resultado do processamento da GTV-e (Modelo 64) + + + + + Dados do protocolo de status + + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que processou a GTV-e + + + + + Chaves de acesso da CT-e + + + + + Data e hora de processamento, no formato AAAA-MM-DDTHH:MM:SS TZD. + + + + + Número do Protocolo de Status da GTV-e + + + + + Digest Value da GTV-e processado. Utilizado para conferir a integridade da GTV-e original. + + + + + Código do status da GTV-e. + + + + + + + + Descrição literal do status da GTV-e. + + + + + + + + + Mensagem do Fisco + + + + + + Código do status da mensagem do fisco + + + + + + + + Mensagem do Fisco + + + + + + + + + + + + + + + + Tipo Retorno do Pedido de Autorização de CT-e Simplificado (Modelo 57) + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Identificação da UF + + + + + Versão do Aplicativo que processou a CT-e + + + + + código do status do retorno da consulta. + + + + + Descrição literal do status do do retorno da consulta. + + + + + Reposta ao processamento do CT-e + + + + + + + + Tipo Retorno do Pedido de Autorização de CT-e (Modelo 57) + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Identificação da UF + + + + + Versão do Aplicativo que processou a CT-e + + + + + código do status do retorno da consulta. + + + + + Descrição literal do status do do retorno da consulta. + + + + + Reposta ao processamento do CT-e + + + + + + + + Tipo Retorno do Pedido de Autorização de GTV-e (Modelo 64) + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Identificação da UF + + + + + Versão do Aplicativo que processou a GTV-e + + + + + código do status do retorno da consulta. + + + + + Descrição literal do status do do retorno da consulta. + + + + + Reposta ao processamento do CT-e + + + + + + + + Tipo Retorno do Pedido de Autorização de CT-e OS (Modelo 67) + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Identificação da UF + + + + + Versão do Aplicativo que processou a CT-e + + + + + código do status do retorno da consulta. + + + + + Descrição literal do status do do retorno da consulta. + + + + + Reposta ao processamento do CT-e + + + + + + + + Tipo Conhecimento de Transporte Eletrônico (Modelo 57) - Modelo Simplificado + + + + + Informações do CT-e + + + + + + Identificação do CT-e + + + + + + Código da UF do emitente do CT-e. + Utilizar a Tabela do IBGE. + + + + + Código numérico que compõe a Chave de Acesso. + Número aleatório gerado pelo emitente para cada CT-e, com o objetivo de evitar acessos indevidos ao documento. + + + + + + + + + + + Código Fiscal de Operações e Prestações + + + + + Natureza da Operação + + + + + + + + + + + Modelo do documento fiscal + Utilizar o código 57 para identificação do CT-e, emitido em substituição aos modelos de conhecimentos em papel. + + + + + Série do CT-e + Preencher com "0" no caso de série única + + + + + + + + Número do CT-e + + + + + Data e hora de emissão do CT-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Formato de impressão do DACTE + Preencher com: 1 - Retrato; 2 - Paisagem. + + + + + + + + + + + + Forma de emissão do CT-e + Preencher com: +1 - Normal; +3 - Regime Especial NFF; +4 - EPEC pela SVC; +7 - Autorização pela SVC-RS; +8 - Autorização pela SVC-SP + + + + + + + + + + + + + + + Digito Verificador da chave de acesso do CT-e + Informar o dígito de controle da chave de acesso do CT-e, que deve ser calculado com a aplicação do algoritmo módulo 11 (base 2,9) da chave de acesso. + + + + + + + + + + + Tipo do Ambiente + Preencher com:1 - Produção; 2 - Homologação. + + + + + Tipo do CT-e Simplificado + Preencher com: +5 - CTe Simplificado +6 - Substituição CTe Simplificado + + + + + Identificador do processo de emissão do CT-e + Preencher com: + 0 - emissão de CT-e com aplicativo do contribuinte; + 3- emissão CT-e pelo contribuinte com aplicativo fornecido pelo SEBRAE. + + + + + Versão do processo de emissão + Informar a versão do aplicativo emissor de CT-e. + + + + + + + + + + + Código do Município de envio do CT-e (de onde o documento foi transmitido) + Utilizar a tabela do IBGE. Informar 9999999 para as operações com o exterior. + + + + + Nome do Município de envio do CT-e (de onde o documento foi transmitido) + Informar PAIS/Municipio para as operações com o exterior. + + + + + + + + + + + Sigla da UF de envio do CT-e (de onde o documento foi transmitido) + Informar 'EX' para operações com o exterior. + + + + + Modal + Preencher com: +01-Rodoviário +02-Aéreo +03-Aquaviário + + + + + Tipo do Serviço + Preencher com: +0 - Normal; +1 - Subcontratação; +2 - Redespacho; + + + + + + + + + + + + + UF do início da prestação + Informar 'EX' para operações com o exterior. + + + + + UF do término da prestação + Informar 'EX' para operações com o exterior. + + + + + Indicador se o Recebedor retira no Aeroporto, Filial, Porto ou Estação de Destino? + Preencher com: 0 - sim; 1 - não + + + + + + + + + + + + Detalhes do retira + + + + + + + + + + + Informar apenas +para tpEmis diferente de 1 + + + + Data e Hora da entrada em contingência + Informar a data e hora no formato AAAA-MM-DDTHH:MM:SS + + + + + Justificativa da entrada em contingência + + + + + + + + + + + + + + + Dados complementares do CT-e para fins operacionais ou comerciais + + + + + + Característica adicional do transporte + Texto livre: +REENTREGA; DEVOLUÇÃO; REFATURAMENTO; etc + + + + + + + + + + + Característica adicional do serviço + Texto livre: + ENTREGA EXPRESSA; LOGÍSTICA REVERSA; CONVENCIONAL; EMERGENCIAL; etc + + + + + + + + + + + Previsão do fluxo da carga + Preenchimento obrigatório para o modal aéreo. + + + + + + Sigla ou código interno da Filial/Porto/Estação/ Aeroporto de Origem + Observações para o modal aéreo: + - Preenchimento obrigatório para o modal aéreo. + - O código de três letras IATA do aeroporto de partida deverá ser incluído como primeira anotação. Quando não for possível, utilizar a sigla OACI. + + + + + + + + + + + + + + Sigla ou código interno da Filial/Porto/Estação/Aeroporto de Passagem + Observação para o modal aéreo: + - O código de três letras IATA, referente ao aeroporto de transferência, deverá ser incluído, quando for o caso. Quando não for possível, utilizar a sigla OACI. Qualquer solicitação de itinerário deverá ser incluída. + + + + + + + + + + + + + + Sigla ou código interno da Filial/Porto/Estação/Aeroporto de Destino + Observações para o modal aéreo: + - Preenchimento obrigatório para o modal aéreo. + - Deverá ser incluído o código de três letras IATA do aeroporto de destino. Quando não for possível, utilizar a sigla OACI. + + + + + + + + + + + Código da Rota de Entrega + + + + + + + + + + + + + + Observações Gerais + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + + + + Identificação do Emitente do CT-e + + + + + + + CNPJ do emitente + Informar zeros não significativos + + + + + CPF do emitente + Informar zeros não significativos. + +Usar com série específica 920-969 para emitente pessoa física com inscrição estadual + + + + + + Inscrição Estadual do Emitente + A IE do emitente somente ficará sem informação para o caso do Regime Especial da NFF (tpEmis=3) + + + + + + + + Inscrição Estadual do Substituto Tributário + + + + + + + + Razão social ou Nome do emitente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Endereço do emitente + + + + + Código do Regime Tributário + Informar: 1=Simples Nacional; +2=Simples Nacional, excesso sublimite de receita bruta; +3=Regime Normal. +4=Simples Nacional - Microempreendedor Individual – MEI. + + + + + + + + + Identificação do tomador do serviço no CT-e + + + + + + Tomador do Serviço + Preencher com: + +0-Remetente; +1-Expedidor; +2-Recebedor; +3-Destinatário +4-Terceiro + + + + + + + + + + + + + + + Indicador do papel do tomador na prestação do serviço: +1 – Contribuinte ICMS; +2 – Contribuinte isento de inscrição; +9 – Não Contribuinte + Aplica-se ao tomador que for indicado no toma + + + + + + + + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. +Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do tomador ou ISENTO se tomador é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o tomador não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão Social ou Nome + + + + + + + + + + + Inscrição na SUFRAMA + (Obrigatório nas operações com as áreas com benefícios de incentivos fiscais sob controle da SUFRAMA) + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + Informações da Carga do CT-e + + + + + + Valor total da carga + + + + + Produto predominante + Informar a descrição do produto predominante + + + + + + + + + + + Outras características da carga + "FRIA", "GRANEL", "REFRIGERADA", "Medidas: 12X12X12" + + + + + + + + + + + Informações de quantidades da Carga do CT-e + Para o Aéreo é obrigatório o preenchimento desse campo da seguinte forma. +1 - Peso Bruto, sempre em quilogramas (obrigatório); +2 - Peso Cubado; sempre em quilogramas; +3 - Quantidade de volumes, sempre em unidades (obrigatório); +4 - Cubagem, sempre em metros cúbicos (obrigatório apenas quando for impossível preencher as dimensões da(s) embalagem(ens) na tag xDime do leiaute do Aéreo). + + + + + + Código da Unidade de Medida + Preencher com: +00-M3; +01-KG; +02-TON; +03-UNIDADE; +04-LITROS; +05-MMBTU + + + + + + + + + + + + + + + + Tipo da Medida + Informar com: +00-Cubagem da NF-e +01-Cubagem Aferida pelo Transportador +02-Peso Bruto da NF-e +03-Peso Bruto Aferido pelo Transportador +04-Peso Cubado +05-Peso Base do Cálculo do Frete +06-Peso para uso Operacional +07-Caixas +08-Paletes +09-Sacas +10-Containers +11-Rolos +12-Bombonas +13-Latas +14-Litragem +15-Milhão de BTU (British Thermal Units) +99-Outros + + + + + + + + + + + + + + + + + + + + + + + + + + + + Quantidade + + + + + + + + Valor da Carga para efeito de averbação + Normalmente igual ao valor declarado da mercadoria, diferente por exemplo, quando a mercadoria transportada é isenta de tributos nacionais para exportação, onde é preciso averbar um valor maior, pois no caso de indenização, o valor a ser pago será maior + + + + + + + + Detalhamento das entregas / prestações do CTe Simplificado + + + + + + + Código do Município de início da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do início da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + + + Código do Município de término da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do término da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + + Valorl da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS + + + + + Valor a Receber + + + + + Componentes do Valor da Prestação + + + + + + Nome do componente + Exxemplos: FRETE PESO, FRETE VALOR, SEC/CAT, ADEME, AGENDAMENTO, etc + + + + + + + + + + + Valor do componente + + + + + + + + + Informações das NF-e + + + + + + Chave de acesso da NF-e + + + + + PIN SUFRAMA + PIN atribuído pela SUFRAMA para a operação. + + + + + + + + + + + + + Data prevista de entrega + Formato AAAA-MM-DD + + + + + + Informações das Unidades de Carga (Containeres/ULD/Outros) + Dispositivo de carga utilizada (Unit Load Device - ULD) significa todo tipo de contêiner de carga, vagão, contêiner de avião, palete de aeronave com rede ou palete de aeronave com rede sobre um iglu. + + + + + Informações das Unidades de Transporte (Carreta/Reboque/Vagão) + Deve ser preenchido com as informações das unidades de transporte utilizadas. + + + + + + + + + Documentos anteriores + + + + + + Chave de acesso do CT-e + + + + + indica se a prestação é total ou parcial em relação as notas do documento anterior + Preencher com: + +1 - Total +2 - Parcial + + + + + + + + + + + + + + + Chave de acesso da NF-e + Informando o tpPrest com “2 – Parcial” deve-se informar as chaves de acesso das NF-e que acobertam a carga transportada. + + + + + + + + + + + + + Número identificador do item agrupador da prestação + + + + + + + + + + + + + Informações do modal + + + + + + XML do modal +Insira neste local o XML específico do modal (rodoviário, aéreo, ferroviário, aquaviário ou dutoviário). + O elemento do tipo -any- permite estender o documento XML com elementos não especificados pelo schema. + Insira neste local - any- o XML específico do modal (rodoviário, aéreo, ferroviário, aquaviário ou dutoviário). A especificação do schema XML para cada modal pode ser encontrada nos arquivos que acompanham este pacote de liberação: + Rodoviário - ver arquivo CTeModalRodoviario_v9.99 + Aéreo - ver arquivo CTeModalAereo_v9.99 + Aquaviário - arquivo CTeModalAquaviario_v9.99 + Ferroviário - arquivo CTeModalFerroviario_v9.99 + Dutoviário - arquivo CTeModalDutoviario_v9.99 + +Onde v9.99 é a a designação genérica para a versão do arquivo. Por exemplo, o arquivo para o schema do modal Rodoviário na versão 1.04 será denominado "CTeModalRodoviario_v1.04". + + + + + + Versão do leiaute específico para o Modal + + + + + + + + + + + + + Dados da cobrança do CT-e + + + + + + Dados da fatura + + + + + + Número da fatura + + + + + + + + + + + Valor original da fatura + + + + + Valor do desconto da fatura + + + + + Valor líquido da fatura + + + + + + + + Dados das duplicatas + + + + + + Número da duplicata + + + + + + + + + + + Data de vencimento da duplicata (AAAA-MM-DD) + + + + + Valor da duplicata + + + + + + + + + + + Informações do CT-e de substituição + + + + + + Chave de acesso do CT-e a ser substituído (original) + + + + + + + + + + Indicador de CT-e Alteração de Tomador + + + + + + + + + + + + + Informações relativas aos Impostos + + + + + + Informações relativas ao ICMS + + + + + + Valor Total dos Tributos + + + + + Informações adicionais de interesse do Fisco + Norma referenciada, informações complementares, etc + + + + + + + + + + + Informações do ICMS de partilha com a UF de término do serviço de transporte na operação interestadual + Grupo a ser informado nas prestações interestaduais para consumidor final, não contribuinte do ICMS + + + + + + Valor da BC do ICMS na UF de término da prestação do serviço de transporte + + + + + Percentual do ICMS relativo ao Fundo de Combate à pobreza (FCP) na UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interna da UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interestadual das UF envolvidas + Alíquota interestadual das UF envolvidas + + + + + + Valor do ICMS relativo ao Fundo de Combate á Pobreza (FCP) da UF de término da prestação + + + + + Valor do ICMS de partilha para a UF de término da prestação do serviço de transporte + + + + + Valor do ICMS de partilha para a UF de início da prestação do serviço de transporte + + + + + + + + + + + Valores Totais do CTe + + + + + + Valor Total da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS + + + + + Valor total a Receber + + + + + + + + Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos. + + + + + + + CNPJ do autorizado + Informar zeros não significativos + + + + + CPF do autorizado + Informar zeros não significativos + + + + + + + + + Informações do Responsável Técnico pela emissão do DF-e + + + + + Grupo de informações do pedido de emissão da Nota Fiscal Fácil + + + + + + Solicitação do pedido de emissão da NFF. + Será preenchido com a totalidade de campos informados no aplicativo emissor serializado. + + + + + + + + + + + + + + Grupo de Informação do Provedor de Assinatura e Autorização + + + + + + CNPJ do Provedor de Assinatura e Autorização + + + + + Assinatura RSA do Emitente para DFe gerados por PAA + + + + + + Assinatura digital padrão RSA + Converter o atributo Id do DFe para array de bytes e assinar com a chave privada do RSA com algoritmo SHA1 gerando um valor no formato base64. + + + + + Chave Publica no padrão XML RSA Key + + + + + + + + + + + + Versão do leiaute + Ex: "4.00" + + + + + + + + Identificador da tag a ser assinada + Informar a chave de acesso do CT-e e precedida do literal "CTe" + + + + + + + + + + + + + + + + Informações suplementares do CT-e + + + + + + Texto com o QR-Code impresso no DACTE + + + + + + + + + + + + + + + + + + + Tipo Conhecimento de Transporte Eletrônico (Modelo 57) + + + + + Informações do CT-e + + + + + + Identificação do CT-e + + + + + + Código da UF do emitente do CT-e. + Utilizar a Tabela do IBGE. + + + + + Código numérico que compõe a Chave de Acesso. + Número aleatório gerado pelo emitente para cada CT-e, com o objetivo de evitar acessos indevidos ao documento. + + + + + + + + + + + Código Fiscal de Operações e Prestações + + + + + Natureza da Operação + + + + + + + + + + + Modelo do documento fiscal + Utilizar o código 57 para identificação do CT-e, emitido em substituição aos modelos de conhecimentos em papel. + + + + + Série do CT-e + Preencher com "0" no caso de série única + + + + + + + + Número do CT-e + + + + + Data e hora de emissão do CT-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Formato de impressão do DACTE + Preencher com: 1 - Retrato; 2 - Paisagem. + + + + + + + + + + + + Forma de emissão do CT-e + Preencher com: +1 - Normal; + 3-Regime Especial NFF; 4-EPEC pela SVC; 5 - Contingência FSDA; + 7 - Autorização pela SVC-RS; + 8 - Autorização pela SVC-SP + + + + + + + + + + + + + + + + Digito Verificador da chave de acesso do CT-e + Informar o dígito de controle da chave de acesso do CT-e, que deve ser calculado com a aplicação do algoritmo módulo 11 (base 2,9) da chave de acesso. + + + + + + + + + + + Tipo do Ambiente + Preencher com:1 - Produção; 2 - Homologação. + + + + + Tipo do CT-e + Preencher com: + 0 - CT-e Normal; + 1 - CT-e de Complemento de Valores; + 3 - CT-e de Substituição + + + + + Identificador do processo de emissão do CT-e + Preencher com: + 0 - emissão de CT-e com aplicativo do contribuinte; + 3- emissão CT-e pelo contribuinte com aplicativo fornecido pelo SEBRAE. + + + + + Versão do processo de emissão + Iinformar a versão do aplicativo emissor de CT-e. + + + + + + + + + + + Indicador de CT-e Globalizado + Informar valor 1 quando for Globalizado e não informar a tag quando não tratar de CT-e Globalizado + + + + + + + + + + Código do Município de envio do CT-e (de onde o documento foi transmitido) + Utilizar a tabela do IBGE. Informar 9999999 para as operações com o exterior. + + + + + Nome do Município de envio do CT-e (de onde o documento foi transmitido) + Informar PAIS/Municipio para as operações com o exterior. + + + + + + + + + + + Sigla da UF de envio do CT-e (de onde o documento foi transmitido) + Informar 'EX' para operações com o exterior. + + + + + Modal + Preencher com:01-Rodoviário; +02-Aéreo;03-Aquaviário;04-Ferroviário;05-Dutoviário;06-Multimodal; + + + + + Tipo do Serviço + Preencher com: +0 - Normal; +1 - Subcontratação; +2 - Redespacho; +3 - Redespacho Intermediário; +4 - Serviço Vinculado a Multimodal + + + + + + + + + + + + + + + Código do Município de início da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do início da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + UF do início da prestação + Informar 'EX' para operações com o exterior. + + + + + Código do Município de término da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do término da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + UF do término da prestação + Informar 'EX' para operações com o exterior. + + + + + Indicador se o Recebedor retira no Aeroporto, Filial, Porto ou Estação de Destino? + Preencher com: 0 - sim; 1 - não + + + + + + + + + + + + Detalhes do retira + + + + + + + + + + + Indicador do papel do tomador na prestação do serviço: +1 – Contribuinte ICMS; +2 – Contribuinte isento de inscrição; +9 – Não Contribuinte + Aplica-se ao tomador que for indicado no toma3 ou toma4 + + + + + + + + + + + + + + Indicador do "papel" do tomador do serviço no CT-e + + + + + + Tomador do Serviço + Preencher com: + 0-Remetente; + 1-Expedidor; + 2-Recebedor; + 3-Destinatário + Serão utilizadas as informações contidas no respectivo grupo, conforme indicado pelo conteúdo deste campo + + + + + + + + + + + + + + + + + Indicador do "papel" do tomador do serviço no CT-e + + + + + + Tomador do Serviço + Preencher com: + 4 - Outros + Obs: Informar os dados cadastrais do tomador do serviço + + + + + + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. +Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do tomador ou ISENTO se tomador é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o tomador não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + + Razão Social ou Nome + + + + + + + + + + + Nome Fantasia + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + + + Informar apenas +para tpEmis diferente de 1 + + + + Data e Hora da entrada em contingência + Informar a data e hora no formato AAAA-MM-DDTHH:MM:SS + + + + + Justificativa da entrada em contingência + + + + + + + + + + + + + + + Dados complementares do CT-e para fins operacionais ou comerciais + + + + + + Característica adicional do transporte + Texto livre: +REENTREGA; DEVOLUÇÃO; REFATURAMENTO; etc + + + + + + + + + + + Característica adicional do serviço + Texto livre: + ENTREGA EXPRESSA; LOGÍSTICA REVERSA; CONVENCIONAL; EMERGENCIAL; etc + + + + + + + + + + + Funcionário emissor do CTe + + + + + + + + + + + Previsão do fluxo da carga + Preenchimento obrigatório para o modal aéreo. + + + + + + Sigla ou código interno da Filial/Porto/Estação/ Aeroporto de Origem + Observações para o modal aéreo: + - Preenchimento obrigatório para o modal aéreo. + - O código de três letras IATA do aeroporto de partida deverá ser incluído como primeira anotação. Quando não for possível, utilizar a sigla OACI. + + + + + + + + + + + + + + Sigla ou código interno da Filial/Porto/Estação/Aeroporto de Passagem + Observação para o modal aéreo: + - O código de três letras IATA, referente ao aeroporto de transferência, deverá ser incluído, quando for o caso. Quando não for possível, utilizar a sigla OACI. Qualquer solicitação de itinerário deverá ser incluída. + + + + + + + + + + + + + + Sigla ou código interno da Filial/Porto/Estação/Aeroporto de Destino + Observações para o modal aéreo: + - Preenchimento obrigatório para o modal aéreo. + - Deverá ser incluído o código de três letras IATA do aeroporto de destino. Quando não for possível, utilizar a sigla OACI. + + + + + + + + + + + Código da Rota de Entrega + + + + + + + + + + + + + + Informações ref. a previsão de entrega + + + + + + + Entrega sem data definida + Esta opção é proibida para o modal aéreo. + + + + + + Tipo de data/período programado para entrega + 0- Sem data definida + + + + + + + + + + + + + + Entrega com data definida + + + + + + Tipo de data/período programado para entrega + Preencher com: + 1-Na data; + 2-Até a data; + 3-A partir da data + + + + + + + + + + + + + Data programada + Formato AAAA-MM-DD + + + + + + + + Entrega no período definido + + + + + + Tipo período + 4-no período + + + + + + + + + + + Data inicial + Formato AAAA-MM-DD + + + + + Data final + Formato AAAA-MM-DD + + + + + + + + + + Entrega sem hora definida + + + + + + Tipo de hora + 0- Sem hora definida + + + + + + + + + + + + + + Entrega com hora definida + + + + + + Tipo de hora + Preencher com: + 1 - No horário; + 2 - Até o horário; + 3 - A partir do horário. + + + + + + + + + + + + + Hora programada + Formato HH:MM:SS + + + + + + + + Entrega no intervalo de horário definido + + + + + + Tipo de hora + 4 - No intervalo de tempo + + + + + + + + + + + Hora inicial + Formato HH:MM:SS + + + + + Hora final + Formato HH:MM:SS + + + + + + + + + + + + Município de origem para efeito de cálculo do frete + + + + + + + + + + + Município de destino para efeito de cálculo do frete + + + + + + + + + + + Observações Gerais + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + + + + Identificação do Emitente do CT-e + + + + + + + CNPJ do emitente + Informar zeros não significativos + + + + + CPF do emitente + Informar zeros não significativos. + +Usar com série específica 920-969 para emitente pessoa física com inscrição estadual + + + + + + Inscrição Estadual do Emitente + A IE do emitente somente ficará sem informação para o caso do Regime Especial da NFF (tpEmis=3) + + + + + + + + Inscrição Estadual do Substituto Tributário + + + + + + + + Razão social ou Nome do emitente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Endereço do emitente + + + + + Código do Regime Tributário + Informar: 1=Simples Nacional; +2=Simples Nacional, excesso sublimite de receita bruta; +3=Regime Normal. +4=Simples Nacional - Microempreendedor Individual – MEI. + + + + + + + + + Informações do Remetente das mercadorias transportadas pelo CT-e + Poderá não ser informado para os CT-e de redespacho intermediário e serviço vinculado a multimodal. Nos demais casos deverá sempre ser informado. + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do remetente ou ISENTO se remetente é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o remetente não seja contribuinte do ICMS não informar a tag. + + + + + + + + Razão social ou nome do remetente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + + + + Informações do Expedidor da Carga + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do expedidor ou ISENTO se expedidor é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o expedidor não seja contribuinte do ICMS não informar a tag. + + + + + + + + Razão Social ou Nome + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + Informações do Recebedor da Carga + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do recebedor ou ISENTO se recebedor é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o recebedor não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão Social ou Nome + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + Informações do Destinatário do CT-e + Poderá não ser informado para os CT-e de redespacho intermediário e serviço vinculado a multimodal. Nos demais casos deverá sempre ser informado. + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do destinatário ou ISENTO se destinatário é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o destinatário não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão Social ou Nome do destinatário + + + + + + + + + + + Telefone + + + + + Inscrição na SUFRAMA + (Obrigatório nas operações com as áreas com benefícios de incentivos fiscais sob controle da SUFRAMA) + + + + + + + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + Valores da Prestação de Serviço + + + + + + Valor Total da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS + + + + + Valor a Receber + + + + + Componentes do Valor da Prestação + + + + + + Nome do componente + Exxemplos: FRETE PESO, FRETE VALOR, SEC/CAT, ADEME, AGENDAMENTO, etc + + + + + + + + + + + Valor do componente + + + + + + + + + + + Informações relativas aos Impostos + + + + + + Informações relativas ao ICMS + + + + + + Valor Total dos Tributos + + + + + Informações adicionais de interesse do Fisco + Norma referenciada, informações complementares, etc + + + + + + + + + + + Informações do ICMS de partilha com a UF de término do serviço de transporte na operação interestadual + Grupo a ser informado nas prestações interestaduais para consumidor final, não contribuinte do ICMS + + + + + + Valor da BC do ICMS na UF de término da prestação do serviço de transporte + + + + + Percentual do ICMS relativo ao Fundo de Combate à pobreza (FCP) na UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interna da UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interestadual das UF envolvidas + Alíquota interestadual das UF envolvidas + + + + + + Valor do ICMS relativo ao Fundo de Combate á Pobreza (FCP) da UF de término da prestação + + + + + Valor do ICMS de partilha para a UF de término da prestação do serviço de transporte + + + + + Valor do ICMS de partilha para a UF de início da prestação do serviço de transporte + + + + + + + + + + + + Grupo de informações do CT-e Normal e Substituto + + + + + + Informações da Carga do CT-e + + + + + + Valor total da carga + Dever ser informado para todos os modais, com exceção para o Dutoviário. + + + + + Produto predominante + Informar a descrição do produto predominante + + + + + + + + + + + Outras características da carga + "FRIA", "GRANEL", "REFRIGERADA", "Medidas: 12X12X12" + + + + + + + + + + + Informações de quantidades da Carga do CT-e + Para o Aéreo é obrigatório o preenchimento desse campo da seguinte forma. +1 - Peso Bruto, sempre em quilogramas (obrigatório); +2 - Peso Cubado; sempre em quilogramas; +3 - Quantidade de volumes, sempre em unidades (obrigatório); +4 - Cubagem, sempre em metros cúbicos (obrigatório apenas quando for impossível preencher as dimensões da(s) embalagem(ens) na tag xDime do leiaute do Aéreo). + + + + + + Código da Unidade de Medida + Preencher com: + 00-M3; + 01-KG; + 02-TON; + 03-UNIDADE; + 04-LITROS; + 05-MMBTU + + + + + + + + + + + + + + + + Tipo da Medida + Exemplos: +PESO BRUTO, PESO DECLARADO, PESO CUBADO, PESO AFORADO, PESO AFERIDO, PESO BASE DE CÁLCULO, LITRAGEM, CAIXAS e etc + + + + + + + + + + + Quantidade + + + + + + + + Valor da Carga para efeito de averbação + Normalmente igual ao valor declarado da mercadoria, diferente por exemplo, quando a mercadoria transportada é isenta de tributos nacionais para exportação, onde é preciso averbar um valor maior, pois no caso de indenização, o valor a ser pago será maior + + + + + + + + Informações dos documentos transportados pelo CT-e +Opcional para Redespacho Intermediario e Serviço vinculado a multimodal. + Poderá não ser informado para os CT-e de redespacho intermediário e serviço vinculado a multimodal. Nos demais casos deverá sempre ser informado. + + + + + + + Informações das NF + Este grupo deve ser informado quando o documento originário for NF + + + + + + Número do Romaneio da NF + + + + + + + + + + + Número do Pedido da NF + + + + + + + + + + + Modelo da Nota Fiscal + Preencher com: +01 - NF Modelo 01/1A e Avulsa; +04 - NF de Produtor + + + + + Série + + + + + + + + + + + Número + + + + + + + + + + + Data de Emissão + Formato AAAA-MM-DD + + + + + Valor da Base de Cálculo do ICMS + + + + + Valor Total do ICMS + + + + + Valor da Base de Cálculo do ICMS ST + + + + + Valor Total do ICMS ST + + + + + Valor Total dos Produtos + + + + + Valor Total da NF + + + + + CFOP Predominante + CFOP da NF ou, na existência de mais de um, predominância pelo critério de valor econômico. + + + + + Peso total em Kg + + + + + PIN SUFRAMA + PIN atribuído pela SUFRAMA para a operação. + + + + + + + + + + + + + Data prevista de entrega + Formato AAAA-MM-DD + + + + + + Informações das Unidades de Carga (Containeres/ULD/Outros) + Dispositivo de carga utilizada (Unit Load Device - ULD) significa todo tipo de contêiner de carga, vagão, contêiner de avião, palete de aeronave com rede ou palete de aeronave com rede sobre um iglu. + + + + + Informações das Unidades de Transporte (Carreta/Reboque/Vagão) + Deve ser preenchido com as informações das unidades de transporte utilizadas. + + + + + + + + + Informações das NF-e + + + + + + Chave de acesso da NF-e + + + + + PIN SUFRAMA + PIN atribuído pela SUFRAMA para a operação. + + + + + + + + + + + + + Data prevista de entrega + Formato AAAA-MM-DD + + + + + + Informações das Unidades de Carga (Containeres/ULD/Outros) + Dispositivo de carga utilizada (Unit Load Device - ULD) significa todo tipo de contêiner de carga, vagão, contêiner de avião, palete de aeronave com rede ou palete de aeronave com rede sobre um iglu. + + + + + Informações das Unidades de Transporte (Carreta/Reboque/Vagão) + Deve ser preenchido com as informações das unidades de transporte utilizadas. + + + + + + + + + Informações dos demais documentos + + + + + + Tipo de documento originário + Preencher com: + 00 - Declaração; + 10 - Dutoviário; + + +59 - CF-e SAT; + +65 - NFC-e; + 99 - Outros + + + + + + + + + + + + + + + Descrição do documento + + + + + + + + + + + Número + + + + + + + + + + + Data de Emissão + Formato AAAA-MM-DD + + + + + Valor do documento + + + + + Data prevista de entrega + Formato AAAA-MM-DD + + + + + + Informações das Unidades de Carga (Containeres/ULD/Outros) + Dispositivo de carga utilizada (Unit Load Device - ULD) significa todo tipo de contêiner de carga, vagão, contêiner de avião, palete de aeronave com rede ou palete de aeronave com rede sobre um iglu. + + + + + Informações das Unidades de Transporte (Carreta/Reboque/Vagão) + Deve ser preenchido com as informações das unidades de transporte utilizadas. + + + + + + + + + + + + + Documentos de Transporte Anterior + + + + + + Emissor do documento anterior + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + + Inscrição Estadual + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + + Razão Social ou Nome do expedidor + + + + + + + + + + + Informações de identificação dos documentos de Transporte Anterior + + + + + + Documentos de transporte anterior em papel + + + + + + Tipo do Documento de Transporte Anterior + Preencher com: +07-ATRE; +08-DTA (Despacho de Transito Aduaneiro); +09-Conhecimento Aéreo Internacional; +10 – Conhecimento - Carta de Porte Internacional; +11 – Conhecimento Avulso; +12-TIF (Transporte Internacional Ferroviário); 13-BL (Bill of Lading) + + + + + + + + Série do Documento Fiscal + + + + + + + + + + + Série do Documento Fiscal + + + + + + + + + + + Número do Documento Fiscal + + + + + + + + + + + Data de emissão (AAAA-MM-DD) + + + + + + + + Documentos de transporte anterior eletrônicos + + + + + + Chave de acesso do CT-e + + + + + + + + + + + + + + + + + Informações do modal + + + + + + XML do modal +Insira neste local o XML específico do modal (rodoviário, aéreo, ferroviário, aquaviário ou dutoviário). + O elemento do tipo -any- permite estender o documento XML com elementos não especificados pelo schema. + Insira neste local - any- o XML específico do modal (rodoviário, aéreo, ferroviário, aquaviário ou dutoviário). A especificação do schema XML para cada modal pode ser encontrada nos arquivos que acompanham este pacote de liberação: + Rodoviário - ver arquivo CTeModalRodoviario_v9.99 + Aéreo - ver arquivo CTeModalAereo_v9.99 + Aquaviário - arquivo CTeModalAquaviario_v9.99 + Ferroviário - arquivo CTeModalFerroviario_v9.99 + Dutoviário - arquivo CTeModalDutoviario_v9.99 + +Onde v9.99 é a a designação genérica para a versão do arquivo. Por exemplo, o arquivo para o schema do modal Rodoviário na versão 1.04 será denominado "CTeModalRodoviario_v1.04". + + + + + + Versão do leiaute específico para o Modal + + + + + + + + + + + + + informações dos veículos transportados + + + + + + Chassi do veículo + + + + + + + + + + + + Cor do veículo + Código de cada montadora + + + + + + + + + + + Descrição da cor + + + + + + + + + + + Código Marca Modelo + Utilizar tabela RENAVAM + + + + + + + + + + + Valor Unitário do Veículo + + + + + Frete Unitário + + + + + + + + Dados da cobrança do CT-e + + + + + + Dados da fatura + + + + + + Número da fatura + + + + + + + + + + + Valor original da fatura + + + + + Valor do desconto da fatura + + + + + Valor líquido da fatura + + + + + + + + Dados das duplicatas + + + + + + Número da duplicata + + + + + + + + + + + Data de vencimento da duplicata (AAAA-MM-DD) + + + + + Valor da duplicata + + + + + + + + + + + Informações do CT-e de substituição + + + + + + Chave de acesso do CT-e a ser substituído (original) + + + + + + + + + + Indicador de CT-e Alteração de Tomador + + + + + + + + + + + + + Informações do CT-e Globalizado + + + + + + Preencher com informações adicionais, legislação do regime especial, etc + + + + + + + + + + + + + + Informações do Serviço Vinculado a Multimodal + + + + + + informações do CT-e multimodal vinculado + + + + + + Chave de acesso do CT-e Multimodal + + + + + + + + + + + + + + Detalhamento do CT-e complementado + + + + + + Chave do CT-e complementado + + + + + + + + + Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos. + + + + + + + CNPJ do autorizado + Informar zeros não significativos + + + + + CPF do autorizado + Informar zeros não significativos + + + + + + + + + Informações do Responsável Técnico pela emissão do DF-e + + + + + Grupo de informações do pedido de emissão da Nota Fiscal Fácil + + + + + + Solicitação do pedido de emissão da NFF. + Será preenchido com a totalidade de campos informados no aplicativo emissor serializado. + + + + + + + + + + + + + + Grupo de Informação do Provedor de Assinatura e Autorização + + + + + + CNPJ do Provedor de Assinatura e Autorização + + + + + Assinatura RSA do Emitente para DFe gerados por PAA + + + + + + Assinatura digital padrão RSA + Converter o atributo Id do DFe para array de bytes e assinar com a chave privada do RSA com algoritmo SHA1 gerando um valor no formato base64. + + + + + Chave Publica no padrão XML RSA Key + + + + + + + + + + + + Versão do leiaute + Ex: "4.00" + + + + + + + + Identificador da tag a ser assinada + Informar a chave de acesso do CT-e e precedida do literal "CTe" + + + + + + + + + + + + Informações suplementares do CT-e + + + + + + Texto com o QR-Code impresso no DACTE + + + + + + + + + + + + + + + + + + + Tipo Conhecimento de Transporte Eletrônico Outros Serviços (Modelo 67) + + + + + Informações do CT-e Outros Serviços + + + + + + Identificação do CT-e Outros Serviços + + + + + + Código da UF do emitente do CT-e. + Utilizar a Tabela do IBGE. + + + + + Código numérico que compõe a Chave de Acesso. + Número aleatório gerado pelo emitente para cada CT-e, com o objetivo de evitar acessos indevidos ao documento. + + + + + + + + + + + Código Fiscal de Operações e Prestações + + + + + Natureza da Operação + + + + + + + + + + + Modelo do documento fiscal + Utilizar o código 67 para identificação do CT-e Outros Serviços, emitido em substituição a Nota Fiscal Modelo 7 para transporte de pessoas, valores e excesso de bagagem. + + + + + Série do CT-e OS + Preencher com "0" no caso de série única + + + + + + + + Número do CT-e OS + + + + + Data e hora de emissão do CT-e OS + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Formato de impressão do DACTE OS + Preencher com: 1 - Retrato; 2 - Paisagem. + + + + + + + + + + + + Forma de emissão do CT-e + Preencher com: +1 - Normal; + 5 - Contingência FSDA; +7 - Autorização pela SVC-RS; + 8 - Autorização pela SVC-SP + + + + + + + + + + + + + + Digito Verificador da chave de acesso do CT-e + Informar o dígito de controle da chave de acesso do CT-e, que deve ser calculado com a aplicação do algoritmo módulo 11 (base 2,9) da chave de acesso. + + + + + + + + + + + Tipo do Ambiente + Preencher com:1 - Produção; 2 - Homologação + + + + + Tipo do CT-e OS + Preencher com: +0 - CT-e Normal; +1 - CT-e Complementar; +3 - CT-e de Substituição. + + + + + Identificador do processo de emissão do CT-e OS + Preencher com: + 0 - emissão de CT-e com aplicativo do contribuinte; + 3- emissão CT-e pelo contribuinte com aplicativo fornecido pelo Fisco. + + + + + Versão do processo de emissão + Iinformar a versão do aplicativo emissor de CT-e. + + + + + + + + + + + Código do Município de envio do CT-e (de onde o documento foi transmitido) + Utilizar a tabela do IBGE. Informar 9999999 para as operações com o exterior. + + + + + Nome do Município de envio do CT-e (de onde o documento foi transmitido) + Informar PAIS/Municipio para as operações com o exterior. + + + + + + + + + + + Sigla da UF de envio do CT-e (de onde o documento foi transmitido) + Informar 'EX' para operações com o exterior. + + + + + Modal do CT-e OS + Preencher com: +01-Rodoviário; +02- Aéreo; +03 - Aquaviário; +04 - Ferroviário. + + + + + Tipo do Serviço + Preencher com: + +6 - Transporte de Pessoas; +7 - Transporte de Valores; +8 - Excesso de Bagagem. + + + + + + + + + + + + + Indicador da IE do tomador: +1 – Contribuinte ICMS; +2 – Contribuinte isento de inscrição; +9 – Não Contribuinte + Aplica-se ao tomador que for indicado no toma3 ou toma4 + + + + + + + + + + + + + Código do Município de início da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do início da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + UF do início da prestação + Informar 'EX' para operações com o exterior. + + + + + Código do Município de término da prestação + Utilizar a tabela do IBGE. Informar 9999999 para operações com o exterior. + + + + + Nome do Município do término da prestação + Informar 'EXTERIOR' para operações com o exterior. + + + + + + + + + + + UF do término da prestação + Informar 'EX' para operações com o exterior. + + + + + Informações do Percurso do CT-e Outros Serviços + + + + + + Sigla das Unidades da Federação do percurso do veículo. + Não é necessário repetir as UF de Início e Fim + + + + + + + + Informar apenas +para tpEmis diferente de 1 + + + + Data e Hora da entrada em contingência + Informar a data e hora no formato AAAA-MM-DDTHH:MM:SS + + + + + Justificativa da entrada em contingência + + + + + + + + + + + + + + + Dados complementares do CT-e para fins operacionais ou comerciais + + + + + + Característica adicional do transporte + Texto livre: +REENTREGA; DEVOLUÇÃO; REFATURAMENTO; etc + + + + + + + + + + + Característica adicional do serviço + Texto livre: + ENTREGA EXPRESSA; LOGÍSTICA REVERSA; CONVENCIONAL; EMERGENCIAL; etc + + + + + + + + + + + Funcionário emissor do CTe + + + + + + + + + + + Observações Gerais + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + Campo de uso livre do contribuinte + Informar o nome do campo no atributo xCampo e o conteúdo do campo no XTexto + + + + + + Conteúdo do campo + + + + + + + + + + + + Identificação do campo + + + + + + + + + + + + + + + + Identificação do Emitente do CT-e OS + + + + + + CNPJ do emitente + Informar zeros não significativos + + + + + Inscrição Estadual do Emitente + + + + + + + + Inscrição Estadual do Substituto Tributário + + + + + + + + Razão social ou Nome do emitente + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Endereço do emitente + + + + + Código do Regime Tributário + Informar: 1=Simples Nacional; +2=Simples Nacional, excesso sublimite de receita bruta; +3=Regime Normal; +4=Simples Nacional - Microempreendedor Individual – MEI. + + + + + + + + + Informações do Tomador/Usuário do Serviço + Opcional para Excesso de Bagagem + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do tomador ou ISENTO se tomador é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o tomador não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Razão social ou nome do tomador + + + + + + + + + + + Nome fantasia + + + + + + + + + + + Telefone + + + + + Dados do endereço + + + + + Endereço de email + + + + + + + + + + + Valores da Prestação de Serviço + + + + + + Valor Total da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS + + + + + Valor a Receber + + + + + Componentes do Valor da Prestação + + + + + + Nome do componente + Exxemplos: FRETE PESO, FRETE VALOR, SEC/CAT, ADEME, AGENDAMENTO, etc + + + + + + + + + + + Valor do componente + + + + + + + + + + + Informações relativas aos Impostos + + + + + + Informações relativas ao ICMS + + + + + + Valor Total dos Tributos + + + + + Informações adicionais de interesse do Fisco + Norma referenciada, informações complementares, etc + + + + + + + + + + + Informações do ICMS de partilha com a UF de término do serviço de transporte na operação interestadual + Grupo a ser informado nas prestações interestaduais para consumidor final, não contribuinte do ICMS + + + + + + Valor da BC do ICMS na UF de término da prestação do serviço de transporte + + + + + Percentual do ICMS relativo ao Fundo de Combate à pobreza (FCP) na UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interna da UF de término da prestação do serviço de transporte + Alíquota adotada nas operações internas na UF do destinatário + + + + + Alíquota interestadual das UF envolvidas + Alíquota interestadual das UF envolvidas + + + + + + Valor do ICMS relativo ao Fundo de Combate á Pobreza (FCP) da UF de término da prestação + + + + + Valor do ICMS de partilha para a UF de término da prestação do serviço de transporte + + + + + Valor do ICMS de partilha para a UF de início da prestação do serviço de transporte + + + + + + + + Informações dos tributos federais + Grupo a ser informado nas prestações interestaduais para consumidor final, não contribuinte do ICMS + + + + + + Valor do PIS + + + + + Valor COFINS + + + + + Valor de Imposto de Renda + + + + + Valor do INSS + + + + + Valor do CSLL + + + + + + + + + + + + Grupo de informações do CT-e OS Normal + + + + + + Informações da Prestação do Serviço + + + + + + Descrição do Serviço prestado + + + + + + + + + + + Informações de quantidades da Carga do CT-e + Para Transporte de Pessoas indicar número de passageiros, para excesso de bagagem e transporte de valores indicar número de Volumes/Malotes + + + + + + Quantidade + + + + + + + + + + + Informações dos documentos referenciados + + + + + + + Número + + + + + + + + + + + Série + + + + + + + + + + + Subsérie + + + + + + + + + + + Data de Emissão + Formato AAAA-MM-DD + + + + + Valor Transportado + + + + + + Chave de acesso do BP-e que possui eventos excesso de bagagem + + + + + + + + + + + Informações de Seguro da Carga + + + + + + Responsável pelo seguro + Preencher com: + +4 - Emitente do CT-e; + +5 - Tomador de Serviço. + + + + + + + + + + + + + + + Nome da Seguradora + + + + + + + + + + + Número da Apólice + Obrigatório pela lei 11.442/07 (RCTRC) + + + + + + + + + + + + + + Informações do modal +Obrigatório para Pessoas e Bagagem + + + + + + XML do modal +Insira neste local o XML específico do modal + O elemento do tipo -any- permite estender o documento XML com elementos não especificados pelo schema. + Insira neste local - any- o XML específico do modal (rodoviário). A especificação do schema XML para cada modal pode ser encontrada nos arquivos que acompanham este pacote de liberação: + Rodoviário - ver arquivo CTeModalRodoviarioOS_v9.99 + +Onde v9.99 é a a designação genérica para a versão do arquivo. Por exemplo, o arquivo para o schema do modal Rodoviário na versão 4.00 será denominado "CTeModalRodoviarioOS_v4.00". + + + + + + Versão do leiaute específico para o Modal + + + + + + + + + + + + + Informações do CT-e de substituição + + + + + + Chave de acesso do CT-e a ser substituído (original) + + + + + + + + + + + + + Chave de acesso do CT-e Cancelado +Somente para Transporte de Valores + + + + + + + + Dados da cobrança do CT-e + + + + + + Dados da fatura + + + + + + Número da fatura + + + + + + + + + + + Valor original da fatura + + + + + Valor do desconto da fatura + + + + + Valor líquido da fatura + + + + + + + + Dados das duplicatas + + + + + + Número da duplicata + + + + + + + + + + + Data de vencimento da duplicata (AAAA-MM-DD) + + + + + Valor da duplicata + + + + + + + + + + + Informações das GTV-e relacionadas ao CT-e OS + + + + + + Chave de acesso da GTV-e + + + + + + + + + + Componentes do Valor da GTVe + + + + + + Tipo do Componente + 1-Custodia +2-Embarque +3-Tempo de espera +4-Malote +5-Ad Valorem +6-Outros + + + + + + + + + + + + + + + + Valor do componente + + + + + Nome do componente (informar apenas para outros) + Exemplos: FRETE PESO, FRETE VALOR, SEC/CAT, ADEME, AGENDAMENTO, etc + + + + + + + + + + + + + + + + + + + + Detalhamento do CT-e complementado + + + + + + Chave do CT-e complementado + + + + + + + + + Autorizados para download do XML do DF-e + Informar CNPJ ou CPF. Preencher os zeros não significativos. + + + + + + + CNPJ do autorizado + Informar zeros não significativos + + + + + CPF do autorizado + Informar zeros não significativos + + + + + + + + + Informações do Responsável Técnico pela emissão do DF-e + + + + + + Versão do leiaute + Ex: "4.00" + + + + + + + + Identificador da tag a ser assinada + Informar a chave de acesso do CT-e OS e precedida do literal "CTe" + + + + + + + + + + + + Informações suplementares do CT-e + + + + + + Texto com o QR-Code impresso no DACTE + + + + + + + + + + + + + + + + + + Versão do leiaute + + + + + + + + + Tipo Dados do Endereço + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município (utilizar a tabela do IBGE) + + + + + Nome do município + + + + + + + + + + + CEP + Informar zeros não significativos + + + + + + + + + + + Sigla da UF + + + + + Telefone + + + + + + + Tipo Dados do Endereço + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município (utilizar a tabela do IBGE) + Informar 9999999 para operações com o exterior. + + + + + Nome do município + Informar EXTERIOR para operações com o exterior. + + + + + + + + + + + CEP + Informar os zeros não significativos + + + + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + Código do país + Utilizar a tabela do BACEN + + + + + + + + + + + Nome do país + + + + + + + + + + + + + Tipo Dados do Endereço + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município (utilizar a tabela do IBGE), informar 9999999 para operações com o exterior. + + + + + Nome do município, , informar EXTERIOR para operações com o exterior. + + + + + + + + + + + CEP + + + + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + + + Tipo Dados do Endereço + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município (utilizar a tabela do IBGE), informar 9999999 para operações com o exterior. + + + + + Nome do município + Informar EXTERIOR para operações com o exterior. + + + + + + + + + + + CEP + + + + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + Código do país + + + + + + + + + + + Nome do país + + + + + + + + + + + Telefone + + + + + + + Tipo Dados do Local de Origem ou Destino + + + + + Código do município (utilizar a tabela do IBGE) + + + + + Nome do município + + + + + + + + + + + Sigla da UF + + + + + + + Tipo Dados do Local de Retirada ou Entrega + + + + + + Número do CNPJ + + + + + Número do CPF + + + + + + Razão Social ou Nome + + + + + + + + + + + Logradouro + + + + + + + + + + + Número + + + + + + + + + + + Complemento + + + + + + + + + + + Bairro + + + + + + + + + + + Código do município (utilizar a tabela do IBGE) + Informar 9999999 para operações com o exterior. + + + + + Nome do município + Informar EXTERIOR para operações com o exterior. + + + + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + + + Tipo Dados do Imposto CT-e + + + + + Prestação sujeito à tributação normal do ICMS + + + + + + classificação Tributária do Serviço + 00 - tributação normal ICMS + + + + + + + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + + + + Prestação sujeito à tributação com redução de BC do ICMS + + + + + + Classificação Tributária do serviço + 20 - tributação com BC reduzida do ICMS + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS Isento, não Tributado ou diferido + + + + + + Classificação Tributária do Serviço + Preencher com: + 40 - ICMS isenção; + 41 - ICMS não tributada; + 51 - ICMS diferido + + + + + + + + + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + Tributação pelo ICMS60 - ICMS cobrado por substituição tributária.Responsabilidade do recolhimento do ICMS atribuído ao tomador ou 3º por ST + + + + + + Classificação Tributária do Serviço + 60 - ICMS cobrado por substituição tributária + + + + + + + + + + + Valor da BC do ICMS ST retido + Valor do frete sobre o qual será calculado o ICMS a ser substituído na Prestação. + + + + + Valor do ICMS ST retido + Resultado da multiplicação do “vBCSTRet” x “pICMSSTRet” – que será valor do ICMS a ser retido pelo Substituto. Podendo o valor do ICMS a ser retido efetivamente, sofrer ajustes conforme a opção tributaria do transportador substituído. + + + + + Alíquota do ICMS + Percentual de Alíquota incidente na prestação de serviço de transporte. + + + + + Valor do Crédito outorgado/Presumido + Preencher somente quando o transportador substituído, for optante pelo crédito outorgado previsto no Convênio 106/96 e corresponde ao percentual de 20% do valor do ICMS ST retido. + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS Outros + + + + + + Classificação Tributária do Serviço + 90 - ICMS outros + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + Valor do Crédito Outorgado/Presumido + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS devido à UF de origem da prestação, quando diferente da UF do emitente + + + + + + Classificação Tributária do Serviço + 90 - ICMS Outra UF + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS devido outra UF + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + Simples Nacional + + + + + + Classificação Tributária do Serviço + 90 - ICMS Simples Nacional + + + + + + + + + + + Indica se o contribuinte é Simples Nacional 1=Sim + + + + + + + + + + + + + + + + Tipo Dados do Imposto para CT-e OS + + + + + Prestação sujeito à tributação normal do ICMS + + + + + + classificação Tributária do Serviço + 00 - tributação normal ICMS + + + + + + + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + + + + Prestação sujeito à tributação com redução de BC do ICMS + + + + + + Classificação Tributária do serviço + 20 - tributação com BC reduzida do ICMS + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS Isento, não Tributado ou diferido + + + + + + Classificação Tributária do Serviço + Preencher com: + 40 - ICMS isenção; + 41 - ICMS não tributada; + 51 - ICMS diferido + + + + + + + + + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS Outros + + + + + + Classificação Tributária do Serviço + 90 - Outros + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS + + + + + Valor do Crédito Outorgado/Presumido + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + ICMS devido à UF de origem da prestação, quando diferente da UF do emitente + + + + + + Classificação Tributária do Serviço + 90 - ICMS Outra UF + + + + + + + + + + + Percentual de redução da BC + + + + + Valor da BC do ICMS + + + + + Alíquota do ICMS + + + + + Valor do ICMS devido outra UF + + + + + Sequencia XML + + + + Valor do ICMS de desoneração + + + + + Código de Benefício Fiscal na UF + Código de Benefício Fiscal utilizado pela UF + + + + + + + + + + + + + + + + Simples Nacional + + + + + + Classificação Tributária do Serviço + 90 - ICMS Simples Nacional + + + + + + + + + + + Indica se o contribuinte é Simples Nacional 1=Sim + + + + + + + + + + + + + + + + Tipo Dados Unidade de Transporte + + + + + Tipo da Unidade de Transporte + 1 - Rodoviário Tração +2 - Rodoviário Reboque +3 - Navio +4 - Balsa +5 - Aeronave +6 - Vagão +7 - Outros + + + + + Identificação da Unidade de Transporte + Informar a identificação conforme o tipo de unidade de transporte. +Por exemplo: para rodoviário tração ou reboque deverá preencher com a placa do veículo. + + + + + + Lacres das Unidades de Transporte + + + + + + Número do lacre + + + + + + + + + + + + + + Informações das Unidades de Carga (Containeres/ULD/Outros) + Dispositivo de carga utilizada (Unit Load Device - ULD) significa todo tipo de contêiner de carga, vagão, contêiner de avião, palete de aeronave com rede ou palete de aeronave com rede sobre um iglu. + + + + + Quantidade rateada (Peso,Volume) + + + + + + + Tipo Dados Unidade de Carga + + + + + Tipo da Unidade de Carga + 1 - Container +2 - ULD +3 - Pallet +4 - Outros + + + + + Identificação da Unidade de Carga + Informar a identificação da unidade de carga, por exemplo: número do container. + + + + + Lacres das Unidades de Carga + + + + + + Número do lacre + + + + + + + + + + + + + + Quantidade rateada (Peso,Volume) + + + + + + + Tipo Dados da Responsável Técnico + + + + + CNPJ da pessoa jurídica responsável técnica pelo sistema utilizado na emissão do documento fiscal eletrônico + Informar o CNPJ da pessoa jurídica desenvolvedora do sistema utilizado na emissão do documento fiscal eletrônico. + + + + + Nome da pessoa a ser contatada + Informar o nome da pessoa a ser contatada na empresa desenvolvedora do sistema utilizado na emissão do documento fiscal eletrônico. No caso de pessoa física, informar o respectivo nome. + + + + + + + + + + + Email da pessoa jurídica a ser contatada + + + + + Telefone da pessoa jurídica a ser contatada + Preencher com o Código DDD + número do telefone. + + + + + + + + + + + + Identificador do código de segurança do responsável técnico + Identificador do CSRT utilizado para geração do hash + + + + + + + + + + Hash do token do código de segurança do responsável técnico + O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) + +Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + + + + + + + + + + + + + Tipo CFOP + + + + + + + + + Tipo Código da Lista de Serviços LC 116/2003 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Número do Container + + + + + + + + + + + Tipo Documento Associado + + + + + + + + + + + + + + + Tipo Email + + + + + + + + + + + Tipo Finalidade da CT-e + + + + + + + + + + + Tipos Finalidade de CT-e Simplificado + + + + + + + + + + Tipo Identificador de controle do envio do lote. Número seqüencial auto-incremental, de controle correspondente ao identificador único do lote enviado. A responsabilidade de gerar e controlar esse número é do próprio contribuinte. + + + + + + + + + Tipo Modelo do Documento + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Modal transporte Outros Serviços + + + + + + + + + + + + Tipo Modal transporte + + + + + + + + + + + + + + Tipo Modal transporte do CTe Simplificado + + + + + + + + + + + Tipo RNTRC - Registro Nacional Transportadores Rodoviários de Carga + + + + + + + + + Tipo CIOT - Código Identificador da Operação de Transporte + + + + + + + + + Tipo Código Regime Tributário + + + + + + + + + + + + + + Tipo processo de emissão do CT-e + + + + + + + + + + Tipo hora + + + + + + + + + Tipo Versão do CT-e - 4.00 + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/cte_v4.00.xsd b/pynfe/data/XSDs/CT-e/cte_v4.00.xsd new file mode 100644 index 00000000..b0cb9c1a --- /dev/null +++ b/pynfe/data/XSDs/CT-e/cte_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Conhecimento de Transporte Eletrônico + + + diff --git a/pynfe/data/XSDs/CT-e/evCCeCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCCeCTe_v4.00.xsd new file mode 100644 index 00000000..92e78690 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCCeCTe_v4.00.xsd @@ -0,0 +1,98 @@ + + + + + + + Schema XML de validação do evento carta de correção +110110 + + + + + + Descrição do Evento - “Carta de Correção” + + + + + + + + + + + + Grupo de Informações de Correção + + + + + + Indicar o grupo de informações que pertence o campoAlterado. Ex: ide + + + + + + + + + + + + Nome do campo modificado do CT-e Original. + + + + + + + + + + + + Valor correspondente à alteração. + + + + + + + + + + + + Preencher com o indice do item alterado caso a alteração ocorra em uma lista. +OBS: O indice inicia sempre em 1 + + + + + + + + + + + + + + Condições de uso da Carta de Correção, + informar a literal :Condições de uso da Carta de Correção, informar a literal: +“A Carta de Correção é disciplinada pelo Art. 58-B do CONVÊNIO/SINIEF 06/89: Fica permitida a utilização de carta de correção, para regularização de erro ocorrido na emissão de documentos fiscais relativos à prestação de serviço de transporte, desde que o erro não esteja relacionado com: I - as variáveis que determinam o valor do imposto tais como: base de cálculo, alíquota, diferença de preço, quantidade, valor da prestação;II - a correção de dados cadastrais que implique mudança do emitente, tomador, remetente ou do destinatário;III - a data de emissão ou de saída.” (texto com acentuação) ou “A Carta de Correcao e disciplinada pelo Art. 58-B do CONVENIO/SINIEF 06/89: Fica permitida a utilizacao de carta de correcao, para regularizacao de erro ocorrido na emissao de documentos fiscais relativos a prestacao de servico de transporte, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da prestacao;II - a correcao de dados cadastrais que implique mudança do emitente, tomador, remetente ou do destinatario;III - a data de emissao ou de saida.” (texto sem acentuação) + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evCECTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCECTe_v4.00.xsd new file mode 100644 index 00000000..17f9c36c --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCECTe_v4.00.xsd @@ -0,0 +1,108 @@ + + + + + + + Schema XML de validação do evento comprovante de entrega eletrônico do CT-e +110180 + + + + + + Descrição do Evento - “Comprovante de Entrega do CT-e” + + + + + + + + + + + Número do Protocolo de autorização do CT-e + + + + + Data e hora de conclusão da entrega da NF-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Número do Documento de identificação da pessoa que recebeu a entrega + + + + + + + + + + + Nome da pessoa que recebeu a entrega + + + + + + + + + + + Latitude do ponto de entrega + + + + + Longitude do ponto de entrega + + + + + Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da entrega (Exemplo: imagem capturada da assinatura eletrônica, digital do recebedor, foto, etc) + O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) +Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + + + + + + + + + + Data e hora de geração do hash entrega + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Grupo de informações das NF-e que foram entregues ao Destinatário + Informar o grupo apenas para CT-e com tipo de serviço Normal + + + + + + Chave de acesso da NF-e entregue + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evCancCECTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCancCECTe_v4.00.xsd new file mode 100644 index 00000000..0a9f3ef5 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCancCECTe_v4.00.xsd @@ -0,0 +1,36 @@ + + + + + + + Schema XML de validação do evento cancelamento do comprovante de entrega eletrônico do CT-e +110181 + + + + + + Descrição do Evento - “Cancelamento do Comprovante de Entrega do CT-e” + + + + + + + + + + + Número do Protocolo de autorização do CT-e + + + + + Número do Protocolo de autorização do evento a ser cancelado + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evCancCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCancCTe_v4.00.xsd new file mode 100644 index 00000000..af1f280b --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCancCTe_v4.00.xsd @@ -0,0 +1,36 @@ + + + + + + + Schema XML de validação do evento do cancelamento +110111 + + + + + + Descrição do Evento - “Cancelamento” + + + + + + + + + + + Número do Protocolo de Status do CT-e + + + + + Justificativa do Cancelamento + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evCancIECTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCancIECTe_v4.00.xsd new file mode 100644 index 00000000..20ce3b5f --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCancIECTe_v4.00.xsd @@ -0,0 +1,36 @@ + + + + + + + Schema XML de validação do evento cancelamento do insucesso de entrega eletrônico do CT-e +110191 + + + + + + Descrição do Evento - “Cancelamento do Insucesso de Entrega do CT-e” + + + + + + + + + + + Número do Protocolo de autorização do CT-e + + + + + Número do Protocolo de autorização do evento a ser cancelado + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evCancPrestDesacordo_v4.00.xsd b/pynfe/data/XSDs/CT-e/evCancPrestDesacordo_v4.00.xsd new file mode 100644 index 00000000..a3404d25 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evCancPrestDesacordo_v4.00.xsd @@ -0,0 +1,32 @@ + + + + + + + Schema XML de validação do evento Cancelamento Prestação do Serviço em Desacordo 610111 + + + + + + Descrição do Evento - “Cancelamento Prestação do Serviço em Desacordo” + + + + + + + + + + + + Protocolo do evento que será cancelado + Informar o número do protocolo de autorização do evento de prestação de serviço em desacordo que será cancelado + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evEPECCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evEPECCTe_v4.00.xsd new file mode 100644 index 00000000..be7452cd --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evEPECCTe_v4.00.xsd @@ -0,0 +1,161 @@ + + + + + + + Schema XML de validação do evento de emissão prévia de emissão em contingência +110113 + + + + + + Descrição do Evento - “EPEC” + + + + + + + + + + + Justificativa da Entrada em Contingencia + + + + + Valor do ICMS + + + + + Valor do ICMS ST + + + + + Valor Total da Prestação do Serviço + Pode conter zeros quando o CT-e for de complemento de ICMS + + + + + Valor total da carga + Dever ser informado para todos os modais, com exceção para o Dutoviário. + + + + + Indicador do "papel" do tomador do serviço no CT-e + + + + + + Tomador do Serviço + Preencher com: +0-Remetente; +1-Expedidor;2-Recebedor;3-Destinatário +;4 - Outros + + + + + + + + + + + + + + + UF do tomador do serviço + Informar 'EX' para operações com o exterior. + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. +Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do tomador ou ISENTO se tomador é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o tomador não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Modal + Preencher com: + +01-Rodoviário; + +02-Aéreo; +03-Aquaviário; + +04-Ferroviário; + +05-Dutoviário; +06-Multimodal; + + + + + UF do início da prestação + Informar 'EX' para operações com o exterior. + + + + + UF do término da prestação + Informar 'EX' para operações com o exterior. + + + + + Tipo do CT-e - Aceitar apenas Tipo Normal = 0 + Preencher com: + 0 - CT-e Normal; + 1 - CT-e de Complemento de Valores; 2 - CT-e de Anulação; + 3 - CT-e Substituto + + + + + + + + + + + + Data e hora de emissão do CT-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evGTV_v4.00.xsd b/pynfe/data/XSDs/CT-e/evGTV_v4.00.xsd new file mode 100644 index 00000000..9b9cd01a --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evGTV_v4.00.xsd @@ -0,0 +1,255 @@ + + + + + + + Schema XML de validação do evento informações da GTV 110170 + + + + + + Descrição do Evento - “Informações da GTV” + + + + + + + + + + + + Grupo de Informações das GTV + + + + + + Número da GTV + + + + + + + + + + + Identificador para diferenciar GTV de mesmo número (Usar número do AIDF ou identificador interno da empresa), + + + + + + + + + + + Série + + + + + + + + + + + Subsérie + + + + + + + + + + + Data de Emissão + Formato AAAA-MM-DD + + + + + Número Dígito Verificador + + + + + + + + + + + Quantidade de volumes/malotes + + + + + Informações das Espécies transportadas + + + + + + Tipo da Espécie + 1 - Numerário +2 - Cheque +3 - Moeda +4 - Outros + + + + + + + + + + + + + + Valor Transportada em Espécie indicada + + + + + + + + Informações do Remetente da GTV + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do remetente ou ISENTO se remetente é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o remetente não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + Razão social ou nome do remetente + + + + + + + + + + + + + + Informações do Destinatário da GTV + + + + + + + Número do CNPJ + Em caso de empresa não estabelecida no Brasil, será informado o CNPJ com zeros. + Informar os zeros não significativos. + + + + + Número do CPF + Informar os zeros não significativos. + + + + + + Inscrição Estadual + Informar a IE do destinatário ou ISENTO se remetente é contribuinte do ICMS isento de inscrição no cadastro de contribuintes do ICMS. Caso o remetente não seja contribuinte do ICMS não informar o conteúdo. + + + + + + + + Sigla da UF + Informar EX para operações com o exterior. + + + + + Razão social ou nome do destinatário + + + + + + + + + + + + + + Placa do veículo + + + + + UF em que veículo está licenciado + Sigla da UF de licenciamento do veículo. + + + + + RNTRC do transportador + + + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evIECTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/evIECTe_v4.00.xsd new file mode 100644 index 00000000..09266903 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evIECTe_v4.00.xsd @@ -0,0 +1,126 @@ + + + + + + + Schema XML de validação do evento insucesso na entrega eletrônico do CT-e +110190 + + + + + + Descrição do Evento - “Insucesso na Entrega do CT-e” + + + + + + + + + + + Número do Protocolo de autorização do CT-e + + + + + Data e hora da tentativa da entrega da NF-e + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Número da tentativa de entrega que não teve insucesso + + + + + + + + + + + Motivo do insucesso + 1- Recebedor não encontrado; +2- Recusa do recebedor; +3- Endereço inexistente; +4- Outros (exige informar justificativa) + + + + + + + + + + + + + + Justificativa do Motivo de insucesso, informar apenas para tpMotivo = 4 + + + + + + + + + + + Latitude do ponto de entrega + + + + + Longitude do ponto de entrega + + + + + Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da tentativa com insucesso da entrega (Exemplo: foto do local que não recebeu a entrega ou do local sem recebedor) + O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) +Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + + + + + + + + + + Data e hora de geração do hash tentativa entrega + Formato AAAA-MM-DDTHH:MM:DD TZD + + + + + + + + Grupo de informações das NF-e que não tiveram sucesso na entrega ao Destinatário + Informar o grupo apenas para CT-e com tipo de serviço Normal + + + + + + Chave de acesso da NF-e com insucesso na tentativa de entrega + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evPrestDesacordo_v4.00.xsd b/pynfe/data/XSDs/CT-e/evPrestDesacordo_v4.00.xsd new file mode 100644 index 00000000..34542d33 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evPrestDesacordo_v4.00.xsd @@ -0,0 +1,49 @@ + + + + + + + Schema XML de validação do evento Prestação do Serviço em Desacordo 610110 + + + + + + Descrição do Evento - “Prestação do Serviço em Desacordo” + + + + + + + + + + + + Indicador de operação em desacordo + + + + + + + + + + Observações do tomador + + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/evRegMultimodal_v4.00.xsd b/pynfe/data/XSDs/CT-e/evRegMultimodal_v4.00.xsd new file mode 100644 index 00000000..6ba3eccd --- /dev/null +++ b/pynfe/data/XSDs/CT-e/evRegMultimodal_v4.00.xsd @@ -0,0 +1,51 @@ + + + + + + + Schema XML de validação do evento Registro Multimodal 110160 + + + + + + Descrição do Evento - “Registro Multimodal” + + + + + + + + + + + Informação complementar sobre o registro, indicação do tipo de documento utilizado e demais situações ocorridas no Multimodal (Texto Livre). + + + + + + + + + + + + + Numero do Documento lançado no CT-e Multimodal + + + + + + + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/eventoCTeTiposBasico_v4.00.xsd b/pynfe/data/XSDs/CT-e/eventoCTeTiposBasico_v4.00.xsd new file mode 100644 index 00000000..d9090707 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/eventoCTeTiposBasico_v4.00.xsd @@ -0,0 +1,331 @@ + + + + + + + + Tipo Evento + + + + + + + + Código do órgão de recepção do Evento. Utilizar a Tabela do IBGE extendida, utilizar 90 para identificar SUFRAMA + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + + CNPJ do emissor do evento + + + + + CPF do emissor do evento + Informar zeros não significativos. + +Usar com série específica 920-969 para emitente pessoa física com inscrição estadual + + + + + + Chave de Acesso do CT-e vinculado ao evento + + + + + Data e Hora do Evento, formato UTC (AAAA-MM-DDThh:mm:ssTZD) + + + + + Tipo do Evento + + + + + + + + + + + Seqüencial do evento para o mesmo tipo de evento. Para maioria dos eventos será 1, nos casos em que possa existir mais de um evento o autor do evento deve numerar de forma seqüencial. + + + + + + + + + + + Detalhamento do evento específico + + + + + + XML do evento +Insira neste local o XML específico do tipo de evento (cancelamento, encerramento, registro de passagem). + + + + + + + + + + + + + + + + Grupo de informações do pedido de registro de evento da Nota Fiscal Fácil + + + + + + Solicitação do pedido de registro de evento da NFF. + Será preenchido com a totalidade de campos informados no aplicativo emissor serializado. + + + + + + + + + + + + + + Grupo de Informação do Provedor de Assinatura e Autorização + + + + + + CNPJ do Provedor de Assinatura e Autorização + + + + + Assinatura RSA do Emitente para DFe gerados por PAA + + + + + + Assinatura digital padrão RSA + Converter o atributo Id do DFe para array de bytes e assinar com a chave privada do RSA com algoritmo SHA1 gerando um valor no formato base64. + + + + + Chave Publica no padrão XML RSA Key + + + + + + + + + + + + Identificador da TAG a ser assinada, a regra de formação do Id é: +“ID” + tpEvento + chave do CT-e + nSeqEvento + + + + + + + + + + + + + + + + Tipo retorno do Evento + + + + + + + + Identificação do Ambiente: +1 - Produção +2 - Homologação + + + + + Versão do Aplicativo que recebeu o Evento + + + + + Código do órgão de recepção do Evento. Utilizar a Tabela do IBGE extendida, utilizar 90 para identificar SUFRAMA + + + + + Código do status da registro do Evento + + + + + Descrição literal do status do registro do Evento + + + + + Chave de Acesso CT-e vinculado + + + + + Tipo do Evento vinculado + + + + + + + + + + + Descrição do Evento + + + + + + + + + + + Seqüencial do evento + + + + + + + + + + + Data e Hora de do recebimento do evento ou do registro do evento formato AAAA-MM-DDThh:mm:ssTZD + + + + + Número do protocolo de registro do evento + + + + + + + + + + + + + + + + + + + + + + + Tipo procEvento + + + + + + + + + IP do transmissor do documento fiscal para o ambiente autorizador + + + + + Porta de origem utilizada na conexão (De 0 a 65535) + + + + + + + + + + Data e Hora da Conexão de Origem + + + + + + Tipo Versão do Evento + + + + + + + + + Tipo Modal transporte + + + + + + + + + + + + + + Tipo número sequencial único do AN + + + + + + diff --git a/pynfe/data/XSDs/CT-e/eventoCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/eventoCTe_v4.00.xsd new file mode 100644 index 00000000..a6197500 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/eventoCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do Pedido de Registro de Evento do CT-e + + + diff --git a/pynfe/data/XSDs/CT-e/procCTeOS_v4.00.xsd b/pynfe/data/XSDs/CT-e/procCTeOS_v4.00.xsd new file mode 100644 index 00000000..c019c2ec --- /dev/null +++ b/pynfe/data/XSDs/CT-e/procCTeOS_v4.00.xsd @@ -0,0 +1,37 @@ + + + + + + + CT-e OS processado + + + + + + + + + + IP do transmissor do documento fiscal para o ambiente autorizador + + + + + Porta de origem utilizada na conexão (De 0 a 65535) + + + + + + + + + + Data e Hora da Conexão de Origem + + + + + diff --git a/pynfe/data/XSDs/CT-e/procCTeSimp_v4.00.xsd b/pynfe/data/XSDs/CT-e/procCTeSimp_v4.00.xsd new file mode 100644 index 00000000..ae7e19cb --- /dev/null +++ b/pynfe/data/XSDs/CT-e/procCTeSimp_v4.00.xsd @@ -0,0 +1,37 @@ + + + + + + + CT-e Simplificado processado + + + + + + + + + + IP do transmissor do documento fiscal para o ambiente autorizador + + + + + Porta de origem utilizada na conexão (De 0 a 65535) + + + + + + + + + + Data e Hora da Conexão de Origem + + + + + diff --git a/pynfe/data/XSDs/CT-e/procCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/procCTe_v4.00.xsd new file mode 100644 index 00000000..c13cb4c8 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/procCTe_v4.00.xsd @@ -0,0 +1,37 @@ + + + + + + + CT-e processado + + + + + + + + + + IP do transmissor do documento fiscal para o ambiente autorizador + + + + + Porta de origem utilizada na conexão (De 0 a 65535) + + + + + + + + + + Data e Hora da Conexão de Origem + + + + + diff --git a/pynfe/data/XSDs/CT-e/procEventoCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/procEventoCTe_v4.00.xsd new file mode 100644 index 00000000..ddfe9d4f --- /dev/null +++ b/pynfe/data/XSDs/CT-e/procEventoCTe_v4.00.xsd @@ -0,0 +1,15 @@ + + + + + + + Pedido de Registro de Eventos de CT-e processado + + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/procGTVe_v4.00.xsd b/pynfe/data/XSDs/CT-e/procGTVe_v4.00.xsd new file mode 100644 index 00000000..b9a43893 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/procGTVe_v4.00.xsd @@ -0,0 +1,37 @@ + + + + + + + GTV-e processada + + + + + + + + + + IP do transmissor do documento fiscal para o ambiente autorizador + + + + + Porta de origem utilizada na conexão (De 0 a 65535) + + + + + + + + + + Data e Hora da Conexão de Origem + + + + + diff --git a/pynfe/data/XSDs/CT-e/retCTeOS_v4.00.xsd b/pynfe/data/XSDs/CT-e/retCTeOS_v4.00.xsd new file mode 100644 index 00000000..3ca744be --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retCTeOS_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno do recibo de envio do CT-e OS (Modelo 67) + + + diff --git a/pynfe/data/XSDs/CT-e/retCTeSimp_v4.00.xsd b/pynfe/data/XSDs/CT-e/retCTeSimp_v4.00.xsd new file mode 100644 index 00000000..8140961c --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retCTeSimp_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno do recibo de envio do CT-e Simplificado (Modelo 57) + + + diff --git a/pynfe/data/XSDs/CT-e/retCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/retCTe_v4.00.xsd new file mode 100644 index 00000000..24f025dd --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno do recibo de envio do CT-e (Modelo 57) + + + diff --git a/pynfe/data/XSDs/CT-e/retConsSitCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/retConsSitCTe_v4.00.xsd new file mode 100644 index 00000000..3e6aac4e --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retConsSitCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno da consulta da situação atual do CT-e. + + + diff --git a/pynfe/data/XSDs/CT-e/retConsStatServCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/retConsStatServCTe_v4.00.xsd new file mode 100644 index 00000000..4bc47d6c --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retConsStatServCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do Resultado da Consulta do Status do Serviço de CT-e + + + diff --git a/pynfe/data/XSDs/CT-e/retEventoCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/retEventoCTe_v4.00.xsd new file mode 100644 index 00000000..41010ef8 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retEventoCTe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno Pedido de Evento do CT-e + + + diff --git a/pynfe/data/XSDs/CT-e/retGTVe_v4.00.xsd b/pynfe/data/XSDs/CT-e/retGTVe_v4.00.xsd new file mode 100644 index 00000000..f4e33316 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/retGTVe_v4.00.xsd @@ -0,0 +1,10 @@ + + + + + + + Schema XML de validação do retorno do recibo de envio da GTV-e (Modelo 64) + + + diff --git a/pynfe/data/XSDs/CT-e/tiposGeralCTe_v4.00.xsd b/pynfe/data/XSDs/CT-e/tiposGeralCTe_v4.00.xsd new file mode 100644 index 00000000..6afdbea9 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/tiposGeralCTe_v4.00.xsd @@ -0,0 +1,647 @@ + + + + + + Data e Hora, formato UTC (AAAA-MM-DDThh:mm:ssTZD, onde TZD = +hh:mm ou -hh:mm) + + + + + + + + + Tipo Ambiente + + + + + + + + + + Tipo ano + + + + + + + + + Tipo Código da UF da tabela do IBGE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Código do Município da tabela do IBGE + + + + + + + + + Tipo Código de orgão (UF da tabela do IBGE + 90 SUFRAMA + 91 RFB + 94 SVC-RS + 95 SVC-SP + 96 Sinc. Chaves do RS para SVSP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Chave de Documento Fiscal Eletrônico + + + + + + + + + + Tipo Número do CNPJ + + + + + + + + + Tipo Número do Telefone + + + + + + + + + Tipo Número do CNPJ tamanho varíavel (3-14) + + + + + + + + + Tipo Número do CNPJ Opcional + + + + + + + + + Tipo Número do CPF + + + + + + + + + Tipo Número do CPF de tamanho variável (3-11) + + + + + + + + + Tipo data AAAA-MM-DD + + + + + + + + + Tipo Decimal com 5 dígitos, sendo 3 de corpo e 2 decimais + + + + + + + + + Tipo Decimal com 6 dígitos, sendo 3 de corpo e 3 decimais + + + + + + + + + Tipo Decimal com 6 ou 5 dígitos, sendo 3 de corpo e 3 ou 2 decimais + + + + + + + + + Tipo Decimal com 5 dígitos, sendo 3 de corpo e 2 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 11 dígitos, sendo 8 de corpo e 3 decimais + + + + + + + + + Tipo Decimal com 11 dígitos, sendo 8 de corpo e 3 decimais utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 12 dígitos, sendo 8 de corpo e 4decimais + + + + + + + + + Tipo Decimal com 12 dígitos, sendo 8 de corpo e 4 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 9 de corpo e 6 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 11 de corpo e 4 decimais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 11 de corpo e 4 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 12 de corpo e 3 decimais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 12 de corpo e 3 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 16 dígitos, sendo 12 de corpo e 4 decimais + + + + + + + + + Tipo Decimal com 16 dígitos, sendo 12 de corpo e 4 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais, utilizado em tags opcionais + + + + + + + + + Tipo Inscrição Estadual do Emitente + + + + + + + + + + Tipo Inscrição Estadual do Destinatário + + + + + + + + + + Tipo Justificativa + + + + + + + + + Tipo temp médio em segundos + + + + + + + + + Tipo Modelo Documento Fiscal + + + + + + + + + Tipo Modelo Documento Fiscal + + + + + + + + + Tipo Modelo Documento Fiscal + + + + + + + + + + Tipo Modelo Documento Fiscal + + + + + + + + + Tipo Modelo Documento Fiscal - NF Remetente + + + + + + + + + + Tipo da Unidade de Transporte + + + + + + + + + + + + + + + Tipo da Unidade de Carga + + + + + + + + + + + + Tipo Motivo + + + + + + + + + Tipo Número do Documento Fiscal + + + + + + + + + Tipo Número do Protocolo de Status + + + + + + + + + Tipo Número do Recibo do envio de lote de NF-e + + + + + + + + + Tipo Série do Documento Fiscal + + + + + + + + + Tipo Serviço solicitado + + + + + + Tipo Código da Mensagem enviada + + + + + + + + + Tipo string genérico + + + + + + + + + Tipo Sigla da UF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Sigla da UF, sem Exterior + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Versão do Aplicativo + + + + + + + + + Coordenada geográfica Latitude + + + + + + + + Coordenada geográfica Longitude + + + + + + + + Tipo IP versão 4 + + + + + + + + + Tipo Placa + + + + + + + + + Tipo que representa uma chave publica padrão RSA + + + + + + + diff --git a/pynfe/data/XSDs/CT-e/xmldsig-core-schema_v1.01.xsd b/pynfe/data/XSDs/CT-e/xmldsig-core-schema_v1.01.xsd new file mode 100644 index 00000000..76b74b38 --- /dev/null +++ b/pynfe/data/XSDs/CT-e/xmldsig-core-schema_v1.01.xsd @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e482421bb3013917db29df9befea9f81dbc2540c Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Mon, 10 Feb 2025 09:53:54 -0300 Subject: [PATCH 002/175] =?UTF-8?q?feat(cte-event):=20evento=20criado=20pa?= =?UTF-8?q?ra=20manifesta=C3=A7=C3=A3o=20de=20destinatario=20para=20cte,?= =?UTF-8?q?=20url=20de=20webservices=20adicionada=20para=20eventos=20de=20?= =?UTF-8?q?sp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/entidades/evento.py | 58 ++++++++++++++++++++++++++++- pynfe/processamento/comunicacao.py | 24 ++++++++++++ pynfe/processamento/serializacao.py | 42 +++++++++++++++++++++ pynfe/utils/webservices.py | 1 + 4 files changed, 123 insertions(+), 2 deletions(-) diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py index 6fac5919..bfd100f9 100644 --- a/pynfe/entidades/evento.py +++ b/pynfe/entidades/evento.py @@ -10,14 +10,14 @@ class Evento(Entidade): # - Identificador da TAG a ser assinada, a regra de formação do Id é: - # “ID” + tpEvento + chave da NF-e + nSeqEvento + # “ID” + tpEvento + chave da NF-e ou do CT-e + nSeqEvento id = str() # - Código do órgão de recepção do Evento. # Utilizar a Tabela do IBGE, utilizar 91 para identificar o Ambiente Nacional. orgao = str() # - CNPJ (obrigatorio) cnpj = str() - # - Chave de Acesso da NF-e vinculada ao Evento + # - Chave de Acesso da NF-e ou CT-e vinculada ao Evento chave = str() # - Data e hora do evento no formato AAAA-MM-DDThh:mm:ssTZD data_emissao = None @@ -235,3 +235,57 @@ def __init__(self, *args, **kwargs): codBanco = str() # - Código da Agência codAgencia = str() + + +class EventoManifestacaoDestCTe(Evento): + """Este serviço permite que o destinatário do Conhecimento de Transporte eletrônico confirme a sua + participação na operação acobertada pelo Conhecimento de Transporte eletrônico emitida para o seu CNPJ + """ + + def __init__(self, *args, **kwargs): + super(EventoManifestacaoDestCTe, self).__init__(*args, **kwargs) + # - numero da operacao + # 1=Comprovante de Entrega do CT-e + # 2=Cancelamento do Comprovante de Entrega do CT-e + # 3=Insucesso na Entrega do CT-e + # 4=Cancelamento do Insucesso de Entrega do CT-e + # 5=Prestação do Serviço em Desacordo + # 6=Cancelamento Prestação do Serviço em Desacordo + dict_tp_evento = {1: "110180", 2: "110181", 3: "110190", 4: "110191", 5: "610110", 6: "610111"} + """ Código do evento + 110180 – Comprovante de Entrega do CT-e + 110181 – Cancelamento do Comprovante de Entrega do CT-e + 110190 – Insucesso na Entrega do CT-e + 110191 – Cancelamento do Insucesso de Entrega do CT-e + 610110 – Prestação do Serviço em Desacordo + 610111 – Cancelamento Prestação do Serviço em Desacordo """ + self.tp_evento = dict_tp_evento[self.operacao] + # - numero da operacao + # 1=Comprovante de Entrega do CT-e + # 2=Cancelamento do Comprovante de Entrega do CT-e + # 3=Insucesso na Entrega do CT-e + # 4=Cancelamento do Insucesso de Entrega do CT-e + # 5=Prestação do Serviço em Desacordo + # 6=Cancelamento Prestação do Serviço em Desacordo + dict_descricao = { + 1: "Comprovante de Entrega do CT-e", + 2: "Cancelamento do Comprovante de Entrega do CT-e", + 3: "Insucesso na Entrega do CT-e", + 4: "Cancelamento do Insucesso de Entrega do CT-e", + 5: "Prestação do Serviço em Desacordo", + 6: "Cancelamento Prestação do Serviço em Desacordo", + } + """ Informar a descrição do evento: + Comprovante de Entrega do CT-e + Cancelamento do Comprovante de Entrega do CT-e + Insucesso na Entrega do CT-e + Cancelamento do Insucesso de Entrega do CT-e + Prestação do Serviço em Desacordo + Cancelamento Prestação do Serviço em Desacordo """ + self.descricao = dict_descricao[self.operacao] + + # - Informar a justificativa porque a operação não foi realizada, + # este campo deve ser informado somente no evento de Insucesso na Entrega do CT-e. + # (min 15 max 255 caracteres) + justificativa = str() + diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 4647eb19..9a1062e8 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1316,6 +1316,28 @@ def _get_url(self, consulta): else: raise Exception(f"Url não encontrada para {consulta} {self.uf.upper()}") return self.url + + def evento(self, modelo, evento): + """ + Envia eventos do MDFe como: + Comprovante de Entrega do CT-e + Cancelamento do Comprovante de Entrega do CT-e + Insucesso na Entrega do CT-e + Cancelamento do Insucesso de Entrega do CT-e + Prestação do Serviço em Desacordo + Cancelamento Prestação do Serviço em Desacordo + :param evento: Nome do Evento + :return: + """ + + # url do serviço + url = self._get_url("EVENTOS") + + # Monta XML do corpo da requisição + raiz = etree.Element(evento, versao="1.00", xmlns=NAMESPACE_CTE) + xml = self._construir_xml_soap("CTeRecepcaoEventoV4", raiz) + return self._post(url, xml) + def _construir_xml_soap(self, metodo, dados, cabecalho=False): """Monta o XML para o envio via SOAP""" @@ -1391,3 +1413,5 @@ def _post(self, url, xml): raise e finally: certificado_a1.excluir() + + diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 5c094d55..8a894294 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2014,6 +2014,48 @@ def serializar_evento_mdfe( return raiz + def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): + tz = datetime.now().astimezone().strftime("%z") + tz = "{}:{}".format(tz[:-2], tz[-2:]) + raiz = etree.Element(tag_raiz, versao=VERSAO_CTE, xmlns=NAMESPACE_CTE) + e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) + etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] + etree.SubElement(e, "tpAmb").text = str(self._ambiente) + if len(so_numeros(evento.cnpj)) == 11: + etree.SubElement(e, "CPF").text = evento.cnpj + else: + etree.SubElement(e, "CNPJ").text = evento.cnpj + etree.SubElement(e, "chCTe").text = evento.chave + etree.SubElement(e, "dhEvento").text = ( + evento.data_emissao.strftime("%Y-%m-%dT%H:%M:%S") + tz + ) + etree.SubElement(e, "tpEvento").text = evento.tp_evento + etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) + #etree.SubElement(e, "verEvento").text = "1.00" #comentei pq no mdfe tava versão cte direto sem essa linha + det = etree.SubElement(e, "detEvento", versao=VERSAO_CTE) + etree.SubElement(det, "descEvento").text = evento.descricao + + # AJUSTAR AQUI TODOS OS CAMPOS PRA CADA EVENTO + if evento.descricao == "Comprovante de Entrega do CT-e": + etree.Subelement(det, "nProt").text = evento.protocolo + elif evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": + etree.Subelement(det, "nProt").text = evento.protocolo + elif evento.descricao == "Insucesso na Entrega do CT-e": + etree.Subelement(det, "nProt").text = evento.protocolo + etree.SubElement(det, "xJust").text = evento.justificativa + elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": + etree.Subelement(det, "nProt").text = evento.protocolo + elif evento.descricao == "Prestação do Serviço em Desacordo": + etree.Subelement(det, "nProt").text = evento.protocolo + elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": + etree.Subelement(det, "nProt").text = evento.protocolo + + if retorna_string: + return etree.tostring(raiz, encoding="unicode", pretty_print=True) + else: + return raiz + + class SerializacaoQrcode(object): """Classe que gera e serializa o qrcode de NFC-e no xml""" diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index db37abdc..91f2f0f0 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -576,6 +576,7 @@ "HOMOLOGACAO": "https://cte-homologacao.", }, "SP": { + "EVENTOS": "fazenda.sp.gov.br/CTeWS/WS/CTeRecepcaoEventoV4.asmx", "STATUS": "fazenda.sp.gov.br/cteWEB/services/cteStatusServico.asmx", "HTTPS": "https://nfe.", "HOMOLOGACAO": "https://homologacao.nfe.", From 321db75d3c35fe8a879ba0f8d892a7ffcb099326 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:36:53 -0300 Subject: [PATCH 003/175] feat(cte-event): adicionado campos para cada tipo especifico de evento de destinatario --- pynfe/processamento/comunicacao.py | 5 ++-- pynfe/processamento/serializacao.py | 43 ++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 9a1062e8..b37dd04e 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1317,7 +1317,7 @@ def _get_url(self, consulta): raise Exception(f"Url não encontrada para {consulta} {self.uf.upper()}") return self.url - def evento(self, modelo, evento): + def evento(self, evento): """ Envia eventos do MDFe como: Comprovante de Entrega do CT-e @@ -1334,8 +1334,7 @@ def evento(self, modelo, evento): url = self._get_url("EVENTOS") # Monta XML do corpo da requisição - raiz = etree.Element(evento, versao="1.00", xmlns=NAMESPACE_CTE) - xml = self._construir_xml_soap("CTeRecepcaoEventoV4", raiz) + xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) return self._post(url, xml) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 8a894294..461c1c9e 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -17,9 +17,11 @@ ) from pynfe.utils.flags import ( CODIGOS_ESTADOS, + NAMESPACE_CTE, NAMESPACE_MDFE, NAMESPACE_NFE, NAMESPACE_SIG, + VERSAO_CTE, VERSAO_MDFE, VERSAO_PADRAO, VERSAO_QRCODE, @@ -2038,17 +2040,50 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # AJUSTAR AQUI TODOS OS CAMPOS PRA CADA EVENTO if evento.descricao == "Comprovante de Entrega do CT-e": etree.Subelement(det, "nProt").text = evento.protocolo + # Data e hora de conclusão da entrega da NF-e, Formato AAAA-MM-DDTHH:MM:DD TZD, + etree.SubElement(det, "dhEntrega").text = evento.data_hora + etree.SubElement(det, "nDoc").text = evento.documento_recebedor + etree.SubElement(det, "xNome").text = evento.nome_recebedor + etree.SubElement(det, "latitude").text = evento.latitude + etree.SubElement(det, "longitude").text = evento.longitude + etree.SubElement(det, "hashEntrega").text = evento.hash_entrega + #Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da entrega (Exemplo: imagem capturada da assinatura eletrônica, digital do recebedor, foto, etc) + #O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) + #Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + etree.SubElement(det, "dhHashEntrega").text = evento.datahora_hash #Formato AAAA-MM-DDTHH:MM:DD TZD + etree.SubElement(det, "infEntrega").text = evento.informacao_entrega #apenas para CT-e com tipo de serviço Normal + etree.SubElement(det, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue elif evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - etree.Subelement(det, "nProt").text = evento.protocolo + etree.Subelement(det, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e + etree.SubElement(det, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado elif evento.descricao == "Insucesso na Entrega do CT-e": etree.Subelement(det, "nProt").text = evento.protocolo - etree.SubElement(det, "xJust").text = evento.justificativa + etree.SubElement(det, "dhTentativaEntrega").text = evento.data_hora_tentativa #Formato AAAA-MM-DDTHH:MM:DD TZD + etree.SubElement(det, "nTentativa").text = evento.numero_tentativa + etree.SubElement(det, "tpMotivo").text = evento.tipo_motivo + #Motivo do insucesso: + # 1- Recebedor não encontrado; + # 2- Recusa do recebedor; + # 3- Endereço inexistente; + # 4- Outros (exige informar justificativa) + etree.SubElement(det, "xJustMotivo").text = evento.justificativa #apenas para tpMotivo = 4, 15-256 caracteres + etree.SubElement(det, "latitude").text = evento.latitude + etree.SubElement(det, "longitude").text = evento.longitude + etree.SubElement(det, "hashTentativaEntrega").text = evento.hash_entrega + # Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da tentativa com insucesso da entrega (Exemplo: foto do local que não recebeu a entrega ou do local sem recebedor) + # O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) + # Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + etree.SubElement(det, "dhHashTentativaEntrega").text = evento.datahora_hash #Formato AAAA-MM-DDTHH:MM:DD TZD + etree.SubElement(det, "infEntrega").text = evento.informacao_entrega #apenas para CT-e com tipo de serviço Normal + etree.SubElement(det, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": etree.Subelement(det, "nProt").text = evento.protocolo + etree.Subelement(det, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - etree.Subelement(det, "nProt").text = evento.protocolo + etree.Subelement(det, "indDesacordoOper").text = evento.indicador_desacordo #Indicador de operação em desacordo + etree.Subelement(det, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - etree.Subelement(det, "nProt").text = evento.protocolo + etree.Subelement(det, "nProtEvPrestDes").text = evento.protocolo_evento if retorna_string: return etree.tostring(raiz, encoding="unicode", pretty_print=True) From 8927610aba162c18efd48a7a4b2c741c0c2f66fe Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:38:22 -0300 Subject: [PATCH 004/175] fix(cte-event): comment untested code and fixed version of serializar_evento_cte to 4.0 --- pynfe/processamento/serializacao.py | 97 ++++++++++++++--------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 461c1c9e..48576087 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao=VERSAO_CTE, xmlns=NAMESPACE_CTE) + raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,57 +2033,56 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - #etree.SubElement(e, "verEvento").text = "1.00" #comentei pq no mdfe tava versão cte direto sem essa linha - det = etree.SubElement(e, "detEvento", versao=VERSAO_CTE) + det = etree.SubElement(e, "detEvento", versaoEvento="4.0") etree.SubElement(det, "descEvento").text = evento.descricao - # AJUSTAR AQUI TODOS OS CAMPOS PRA CADA EVENTO - if evento.descricao == "Comprovante de Entrega do CT-e": - etree.Subelement(det, "nProt").text = evento.protocolo - # Data e hora de conclusão da entrega da NF-e, Formato AAAA-MM-DDTHH:MM:DD TZD, - etree.SubElement(det, "dhEntrega").text = evento.data_hora - etree.SubElement(det, "nDoc").text = evento.documento_recebedor - etree.SubElement(det, "xNome").text = evento.nome_recebedor - etree.SubElement(det, "latitude").text = evento.latitude - etree.SubElement(det, "longitude").text = evento.longitude - etree.SubElement(det, "hashEntrega").text = evento.hash_entrega - #Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da entrega (Exemplo: imagem capturada da assinatura eletrônica, digital do recebedor, foto, etc) - #O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) - #Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary - etree.SubElement(det, "dhHashEntrega").text = evento.datahora_hash #Formato AAAA-MM-DDTHH:MM:DD TZD - etree.SubElement(det, "infEntrega").text = evento.informacao_entrega #apenas para CT-e com tipo de serviço Normal - etree.SubElement(det, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue - elif evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - etree.Subelement(det, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e - etree.SubElement(det, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado - elif evento.descricao == "Insucesso na Entrega do CT-e": - etree.Subelement(det, "nProt").text = evento.protocolo - etree.SubElement(det, "dhTentativaEntrega").text = evento.data_hora_tentativa #Formato AAAA-MM-DDTHH:MM:DD TZD - etree.SubElement(det, "nTentativa").text = evento.numero_tentativa - etree.SubElement(det, "tpMotivo").text = evento.tipo_motivo - #Motivo do insucesso: - # 1- Recebedor não encontrado; - # 2- Recusa do recebedor; - # 3- Endereço inexistente; - # 4- Outros (exige informar justificativa) - etree.SubElement(det, "xJustMotivo").text = evento.justificativa #apenas para tpMotivo = 4, 15-256 caracteres - etree.SubElement(det, "latitude").text = evento.latitude - etree.SubElement(det, "longitude").text = evento.longitude - etree.SubElement(det, "hashTentativaEntrega").text = evento.hash_entrega - # Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da tentativa com insucesso da entrega (Exemplo: foto do local que não recebeu a entrega ou do local sem recebedor) - # O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) - # Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary - etree.SubElement(det, "dhHashTentativaEntrega").text = evento.datahora_hash #Formato AAAA-MM-DDTHH:MM:DD TZD - etree.SubElement(det, "infEntrega").text = evento.informacao_entrega #apenas para CT-e com tipo de serviço Normal - etree.SubElement(det, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega - elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - etree.Subelement(det, "nProt").text = evento.protocolo - etree.Subelement(det, "nProtIE").text = evento.protocolo_evento - elif evento.descricao == "Prestação do Serviço em Desacordo": - etree.Subelement(det, "indDesacordoOper").text = evento.indicador_desacordo #Indicador de operação em desacordo - etree.Subelement(det, "xObs").text = evento.observacao + # EVENTOS COMENTADOS NÂO TESTADOS + # if evento.descricao == "Comprovante de Entrega do CT-e": + # etree.Subelement(det, "nProt").text = evento.protocolo + # # Data e hora de conclusão da entrega da NF-e, Formato AAAA-MM-DDTHH:MM:DD TZD, + # etree.SubElement(det, "dhEntrega").text = evento.data_hora.strftime("%Y-%m-%dT%H:%M:%S") + tz + # etree.SubElement(det, "nDoc").text = evento.documento_recebedor + # etree.SubElement(det, "xNome").text = evento.nome_recebedor + # etree.SubElement(det, "latitude").text = evento.latitude + # etree.SubElement(det, "longitude").text = evento.longitude + # etree.SubElement(det, "hashEntrega").text = evento.hash_entrega # concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da entrega (Exemplo: imagem capturada da assinatura eletrônica, digital do recebedor, foto, etc), resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) + # etree.SubElement(det, "dhHashEntrega").text = evento.datahora_hash.strftime("%Y-%m-%dT%H:%M:%S") + tz #Formato AAAA-MM-DDTHH:MM:DD TZD + # if evento.informacao_entrega: + # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal + # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue + # elif evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": + # etree.SubElement(det, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e + # etree.SubElement(det, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado + # elif evento.descricao == "Insucesso na Entrega do CT-e": + # etree.SubElement(det, "nProt").text = evento.protocolo + # etree.SubElement(det, "dhTentativaEntrega").text = evento.data_hora_tentativa.strftime("%Y-%m-%dT%H:%M:%S") + tz #Formato AAAA-MM-DDTHH:MM:DD TZD + # etree.SubElement(det, "nTentativa").text = evento.numero_tentativa + # etree.SubElement(det, "tpMotivo").text = evento.tipo_motivo + # #Motivo do insucesso: + # # 1- Recebedor não encontrado; + # # 2- Recusa do recebedor; + # # 3- Endereço inexistente; + # # 4- Outros (exige informar justificativa) + # if evento.tipo_motivo == 4: + # etree.SubElement(det, "xJustMotivo").text = evento.justificativa #apenas para tpMotivo = 4, 15-256 caracteres + # etree.SubElement(det, "latitude").text = evento.latitude + # etree.SubElement(det, "longitude").text = evento.longitude + # etree.SubElement(det, "hashTentativaEntrega").text = evento.hash_entrega + # # Hash (SHA1) no formato Base64 resultante da concatenação: Chave de acesso do CT-e + Base64 da imagem capturada da tentativa com insucesso da entrega (Exemplo: foto do local que não recebeu a entrega ou do local sem recebedor) + # # O hashCSRT é o resultado das funções SHA-1 e base64 do token CSRT fornecido pelo fisco + chave de acesso do DF-e. (Implementação em futura NT) + # # Observação: 28 caracteres são representados no schema como 20 bytes do tipo base64Binary + # etree.SubElement(det, "dhHashTentativaEntrega").text = evento.datahora_hash.strftime("%Y-%m-%dT%H:%M:%S") + tz #Formato AAAA-MM-DDTHH:MM:DD TZD + # if evento.informacao_entrega: + # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal + # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega + # elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": + # etree.SubElement(det, "nProt").text = evento.protocolo + # etree.SubElement(det, "nProtIE").text = evento.protocolo_evento + if evento.descricao == "Prestação do Serviço em Desacordo": + etree.SubElement(det, "indDesacordoOper").text = 1 #Indicador de operação em desacordo + etree.SubElement(det, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - etree.Subelement(det, "nProtEvPrestDes").text = evento.protocolo_evento + etree.SubElement(det, "nProtEvPrestDes").text = evento.protocolo_evento if retorna_string: return etree.tostring(raiz, encoding="unicode", pretty_print=True) From 62e8170bfafb517f78113c5ad310a051cdda3812 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:56:14 -0300 Subject: [PATCH 005/175] =?UTF-8?q?fix(cte-event):=20fixed=20int=20value?= =?UTF-8?q?=20to=20string=20value=20for=20xml=20indicador=20de=20opera?= =?UTF-8?q?=C3=A7=C3=A3o=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- pynfe/processamento/serializacao.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index b37dd04e..78d04fcc 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1319,7 +1319,7 @@ def _get_url(self, consulta): def evento(self, evento): """ - Envia eventos do MDFe como: + Envia eventos do CTe como: Comprovante de Entrega do CT-e Cancelamento do Comprovante de Entrega do CT-e Insucesso na Entrega do CT-e diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 48576087..ef8c2dd6 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2079,7 +2079,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # etree.SubElement(det, "nProt").text = evento.protocolo # etree.SubElement(det, "nProtIE").text = evento.protocolo_evento if evento.descricao == "Prestação do Serviço em Desacordo": - etree.SubElement(det, "indDesacordoOper").text = 1 #Indicador de operação em desacordo + etree.SubElement(det, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(det, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": etree.SubElement(det, "nProtEvPrestDes").text = evento.protocolo_evento From 136ba9e520217d16aa3d0841ea735af6a92b60cd Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:50:35 -0300 Subject: [PATCH 006/175] feat(cte-event): added names of events for det --- pynfe/processamento/serializacao.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index ef8c2dd6..5bcd00b9 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2050,9 +2050,10 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # if evento.informacao_entrega: # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue - # elif evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - # etree.SubElement(det, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e - # etree.SubElement(det, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado + if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": + cancelamento = etree.SubElement(det, "evCancCECTe") + etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e + etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado # elif evento.descricao == "Insucesso na Entrega do CT-e": # etree.SubElement(det, "nProt").text = evento.protocolo # etree.SubElement(det, "dhTentativaEntrega").text = evento.data_hora_tentativa.strftime("%Y-%m-%dT%H:%M:%S") + tz #Formato AAAA-MM-DDTHH:MM:DD TZD @@ -2075,14 +2076,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # if evento.informacao_entrega: # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega - # elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - # etree.SubElement(det, "nProt").text = evento.protocolo - # etree.SubElement(det, "nProtIE").text = evento.protocolo_evento - if evento.descricao == "Prestação do Serviço em Desacordo": - etree.SubElement(det, "indDesacordoOper").text = "1" #Indicador de operação em desacordo - etree.SubElement(det, "xObs").text = evento.observacao + elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": + cancelamento = etree.SubElement(det, "evCancIECTe") + etree.SubElement(cancelamento, "nProt").text = evento.protocolo + etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento + elif evento.descricao == "Prestação do Serviço em Desacordo": + desacordo = etree.SubElement(det, "evPrestDesacordo") + etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo + etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - etree.SubElement(det, "nProtEvPrestDes").text = evento.protocolo_evento + cancelamento = etree.SubElement(det, "evCancPrestDesacordo") + etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento if retorna_string: return etree.tostring(raiz, encoding="unicode", pretty_print=True) From a424af468ba71a54eff194a692d5fd4d5cb38770 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:18:54 -0300 Subject: [PATCH 007/175] fix(cte-event): fix descEvento --- pynfe/processamento/serializacao.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 5bcd00b9..f90ff89f 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2034,8 +2034,6 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) det = etree.SubElement(e, "detEvento", versaoEvento="4.0") - etree.SubElement(det, "descEvento").text = evento.descricao - # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo @@ -2052,6 +2050,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": cancelamento = etree.SubElement(det, "evCancCECTe") + etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado # elif evento.descricao == "Insucesso na Entrega do CT-e": @@ -2078,14 +2077,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": cancelamento = etree.SubElement(det, "evCancIECTe") + etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": desacordo = etree.SubElement(det, "evPrestDesacordo") + etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": cancelamento = etree.SubElement(det, "evCancPrestDesacordo") + etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento if retorna_string: From 66ec54d8864d11b5def59eb60bcce32732a2edf8 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:08:46 -0300 Subject: [PATCH 008/175] feat(testing-comunicacao): added debbuger print --- pynfe/processamento/comunicacao.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 78d04fcc..3c3328f1 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1395,6 +1395,9 @@ def _post(self, url, xml): ) xml = xml_declaration + xml + xml_text = etree.tostring(xml, encoding="unicode", pretty_print=True) + print(f"xml_text:{xml_text}") + # Faz o request com o servidor result = requests.post( url, From 37d7615f0a29c2a5b7071681a432671cb7fe809e Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:18:55 -0300 Subject: [PATCH 009/175] fix(testing-comunicacao): it was already string ahaha --- pynfe/processamento/comunicacao.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 3c3328f1..0b84d090 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1395,8 +1395,7 @@ def _post(self, url, xml): ) xml = xml_declaration + xml - xml_text = etree.tostring(xml, encoding="unicode", pretty_print=True) - print(f"xml_text:{xml_text}") + print(xml) # Faz o request com o servidor result = requests.post( From 0c9f5706bff340df895926f1b127455965f53895 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 15:56:24 -0300 Subject: [PATCH 010/175] fix(main): fix --- pynfe/processamento/comunicacao.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 0b84d090..ab861f33 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1334,8 +1334,8 @@ def evento(self, evento): url = self._get_url("EVENTOS") # Monta XML do corpo da requisição - xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) - return self._post(url, xml) + #xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) + return self._post(url, evento) def _construir_xml_soap(self, metodo, dados, cabecalho=False): @@ -1394,7 +1394,6 @@ def _post(self, url, xml): etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml - print(xml) # Faz o request com o servidor From 723762ca6b652b7b713e7180581c16df87a9232f Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:19:47 -0300 Subject: [PATCH 011/175] fix(main): added soap --- pynfe/processamento/comunicacao.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ab861f33..ebc3c7ca 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1334,8 +1334,8 @@ def evento(self, evento): url = self._get_url("EVENTOS") # Monta XML do corpo da requisição - #xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) - return self._post(url, evento) + xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) + return self._post(url, xml) def _construir_xml_soap(self, metodo, dados, cabecalho=False): @@ -1343,7 +1343,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, + nsmap={"soap": NAMESPACE_SOAP}, #"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, ) if self._header: From 3adaba0fdeff1249a5a8185d13e4948a9eb8de08 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:46:15 -0300 Subject: [PATCH 012/175] fix(main): versaoDados to 4.0 --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ebc3c7ca..9ee8160f 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1260,7 +1260,7 @@ def _cabecalho_soap(self, metodo): raiz = etree.Element(self._header, xmlns=self._namespace_metodo + metodo) etree.SubElement(raiz, "cUF").text = CODIGOS_ESTADOS[self.uf.upper()] - etree.SubElement(raiz, "versaoDados").text = "3.00" + etree.SubElement(raiz, "versaoDados").text = "4.0" return raiz def _get_url(self, consulta): From 8190b74fc73bac3b897949f48b20905c68441722 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 20:58:02 -0300 Subject: [PATCH 013/175] fix(main): testing with version 3.0 --- pynfe/processamento/comunicacao.py | 2 +- pynfe/processamento/serializacao.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 9ee8160f..7f1b984e 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1260,7 +1260,7 @@ def _cabecalho_soap(self, metodo): raiz = etree.Element(self._header, xmlns=self._namespace_metodo + metodo) etree.SubElement(raiz, "cUF").text = CODIGOS_ESTADOS[self.uf.upper()] - etree.SubElement(raiz, "versaoDados").text = "4.0" + etree.SubElement(raiz, "versaoDados").text = "3.0" return raiz def _get_url(self, consulta): diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index f90ff89f..334348ca 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE) + raiz = etree.Element(tag_raiz, versao="3.0", xmlns=NAMESPACE_CTE) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,7 +2033,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="4.0") + det = etree.SubElement(e, "detEvento", versaoEvento="3.0") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo From 57ced00f5e48434835677726b796f696b6b0c81e Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 21:18:48 -0300 Subject: [PATCH 014/175] fix(main): version 4.00 not 4.0 --- pynfe/processamento/comunicacao.py | 2 +- pynfe/processamento/serializacao.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 7f1b984e..d66b12ff 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1260,7 +1260,7 @@ def _cabecalho_soap(self, metodo): raiz = etree.Element(self._header, xmlns=self._namespace_metodo + metodo) etree.SubElement(raiz, "cUF").text = CODIGOS_ESTADOS[self.uf.upper()] - etree.SubElement(raiz, "versaoDados").text = "3.0" + etree.SubElement(raiz, "versaoDados").text = "4.00" return raiz def _get_url(self, consulta): diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 334348ca..c5a1c69b 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="3.0", xmlns=NAMESPACE_CTE) + raiz = etree.Element(tag_raiz, versao="4.00", xmlns=NAMESPACE_CTE) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,7 +2033,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="3.0") + det = etree.SubElement(e, "detEvento", versaoEvento="4.00") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo From bcced62c1956d25f1663e516cc04a15fd5d94f6c Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 21:56:02 -0300 Subject: [PATCH 015/175] fix(main): xsi and xsd --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index d66b12ff..30c4d475 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1343,7 +1343,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"soap": NAMESPACE_SOAP}, #"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, + nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, ) if self._header: From 00abf31283a751e28f52d7b06e8f4f0d7742066b Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:08:17 -0300 Subject: [PATCH 016/175] fix(main): fix --- pynfe/processamento/serializacao.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index c5a1c69b..f90ff89f 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.00", xmlns=NAMESPACE_CTE) + raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,7 +2033,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="4.00") + det = etree.SubElement(e, "detEvento", versaoEvento="4.0") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo From 2be1cb81fe964d89e5bddec55329cbb751aa5ab6 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:09:19 -0300 Subject: [PATCH 017/175] fix(main): added namespaces --- pynfe/processamento/comunicacao.py | 4 ++-- pynfe/processamento/serializacao.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 30c4d475..2b49e1fb 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -16,6 +16,7 @@ NAMESPACE_MDFE_METODO, NAMESPACE_METODO, NAMESPACE_NFE, + NAMESPACE_SIG, NAMESPACE_SOAP, NAMESPACE_XSD, NAMESPACE_XSI, @@ -1343,9 +1344,8 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, + nsmap={"ds": NAMESPACE_SIG, "xs": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, #"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD ) - if self._header: cabecalho = self._cabecalho_soap(metodo) c = etree.SubElement(raiz, "{%s}Header" % self._namespace_soap) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index f90ff89f..80acd593 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE) + raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE, xmlns_ds=NAMESPACE_SIG) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) From f6cc144831e0b2430b3c86ea34bbcf2cd310850b Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:35:16 -0300 Subject: [PATCH 018/175] fix(main): added any tag at det --- pynfe/processamento/serializacao.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 80acd593..d92cd38a 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.0", xmlns=NAMESPACE_CTE, xmlns_ds=NAMESPACE_SIG) + raiz = etree.Element(tag_raiz, versao="4.00", xmlns=NAMESPACE_CTE, xmlns_ds=NAMESPACE_SIG) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,7 +2033,8 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="4.0") + det = etree.SubElement(e, "detEvento", versaoEvento="4.00") + det_any = etree.SubElement(det, "any") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo @@ -2049,7 +2050,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancCECTe") + cancelamento = etree.SubElement(det_any, "evCancCECTe") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado @@ -2076,17 +2077,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancIECTe") + cancelamento = etree.SubElement(det_any, "evCancIECTe") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - desacordo = etree.SubElement(det, "evPrestDesacordo") + desacordo = etree.SubElement(det_any, "evPrestDesacordo") etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - cancelamento = etree.SubElement(det, "evCancPrestDesacordo") + cancelamento = etree.SubElement(det_any, "evCancPrestDesacordo") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento From 646f241852e5108d268f7b00c6030f33427630c9 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 08:16:53 -0300 Subject: [PATCH 019/175] fix(main): added namespace to det event --- pynfe/processamento/comunicacao.py | 2 +- pynfe/processamento/serializacao.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 2b49e1fb..a5a20a1a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1344,7 +1344,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"ds": NAMESPACE_SIG, "xs": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, #"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD + nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, ) if self._header: cabecalho = self._cabecalho_soap(metodo) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index d92cd38a..a1005c74 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2034,7 +2034,6 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) det = etree.SubElement(e, "detEvento", versaoEvento="4.00") - det_any = etree.SubElement(det, "any") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo @@ -2050,7 +2049,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - cancelamento = etree.SubElement(det_any, "evCancCECTe") + cancelamento = etree.SubElement(det, "evCancCECTe", xmlns = NAMESPACE_CTE) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado @@ -2077,17 +2076,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - cancelamento = etree.SubElement(det_any, "evCancIECTe") + cancelamento = etree.SubElement(det, "evCancIECTe", xmlns = NAMESPACE_CTE) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - desacordo = etree.SubElement(det_any, "evPrestDesacordo") + desacordo = etree.SubElement(det, "evPrestDesacordo", xmlns = NAMESPACE_CTE) etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - cancelamento = etree.SubElement(det_any, "evCancPrestDesacordo") + cancelamento = etree.SubElement(det, "evCancPrestDesacordo", xmlns = NAMESPACE_CTE) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento From 476b2ef2e4a479be72c2c515a3af3fb109f23dd2 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 08:20:42 -0300 Subject: [PATCH 020/175] fix(main): namespace ds to event nsmap --- pynfe/processamento/serializacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index a1005c74..aa0a4d49 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.00", xmlns=NAMESPACE_CTE, xmlns_ds=NAMESPACE_SIG) + raiz = etree.Element(tag_raiz, versao="4.00", nsmap={None: NAMESPACE_CTE, "ds": NAMESPACE_SIG}) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) From a7c6384dff15b252c1d565a6f17bb630a91b5720 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 08:33:07 -0300 Subject: [PATCH 021/175] fix(main): removed header of soap --- pynfe/processamento/comunicacao.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index a5a20a1a..e38d82a8 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1346,10 +1346,10 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): "{%s}Envelope" % NAMESPACE_SOAP, nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, ) - if self._header: - cabecalho = self._cabecalho_soap(metodo) - c = etree.SubElement(raiz, "{%s}Header" % self._namespace_soap) - c.append(cabecalho) + # if self._header: + # cabecalho = self._cabecalho_soap(metodo) + # c = etree.SubElement(raiz, "{%s}Header" % self._namespace_soap) + # c.append(cabecalho) body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) # distribuição tem um corpo de xml diferente From baf91abfd8b902a4546aac2d393b61fead603af1 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 08:46:33 -0300 Subject: [PATCH 022/175] fix(main): nsmap at the det event --- pynfe/processamento/serializacao.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index aa0a4d49..ae5ab3e2 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2049,7 +2049,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancCECTe", xmlns = NAMESPACE_CTE) + cancelamento = etree.SubElement(det, "evCancCECTe", nsmap={None: NAMESPACE_CTE}) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado @@ -2076,17 +2076,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancIECTe", xmlns = NAMESPACE_CTE) + cancelamento = etree.SubElement(det, "evCancIECTe", nsmap={None: NAMESPACE_CTE}) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - desacordo = etree.SubElement(det, "evPrestDesacordo", xmlns = NAMESPACE_CTE) + desacordo = etree.SubElement(det, "evPrestDesacordo", nsmap={None: NAMESPACE_CTE}) etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - cancelamento = etree.SubElement(det, "evCancPrestDesacordo", xmlns = NAMESPACE_CTE) + cancelamento = etree.SubElement(det, "evCancPrestDesacordo", nsmap={None: NAMESPACE_CTE}) etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento From a6dadcaa560f743532a4b8c4b58101f93d798c8b Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 09:05:53 -0300 Subject: [PATCH 023/175] fix(main): fix --- pynfe/processamento/serializacao.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index ae5ab3e2..999394fd 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2033,7 +2033,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="4.00") + det = etree.SubElement(e, "detEvento", versaoEvento="4.00", nsmap={None:NAMESPACE_CTE }) # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo @@ -2049,7 +2049,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e entregue if evento.descricao == "Cancelamento do Comprovante de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancCECTe", nsmap={None: NAMESPACE_CTE}) + cancelamento = etree.SubElement(det, "evCancCECTe") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo #Número do Protocolo de autorização do CT-e etree.SubElement(cancelamento, "nProtCE").text = evento.protocolo_evento #Número do Protocolo de autorização do evento a ser cancelado @@ -2076,17 +2076,17 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal # inf_entrega = etree.SubElement(det, "infEntrega") #apenas para CT-e com tipo de serviço Normal # etree.SubElement(inf_entrega, "chNFe").text = evento.chave_acesso #chave de acesso da NF-e com insucesso na entrega elif evento.descricao == "Cancelamento do Insucesso de Entrega do CT-e": - cancelamento = etree.SubElement(det, "evCancIECTe", nsmap={None: NAMESPACE_CTE}) + cancelamento = etree.SubElement(det, "evCancIECTe") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - desacordo = etree.SubElement(det, "evPrestDesacordo", nsmap={None: NAMESPACE_CTE}) + desacordo = etree.SubElement(det, "evPrestDesacordo", nsmap={None:NAMESPACE_CTE}) etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao elif evento.descricao == "Cancelamento Prestação do Serviço em Desacordo": - cancelamento = etree.SubElement(det, "evCancPrestDesacordo", nsmap={None: NAMESPACE_CTE}) + cancelamento = etree.SubElement(det, "evCancPrestDesacordo") etree.SubElement(cancelamento, "descEvento").text = evento.descricao etree.SubElement(cancelamento, "nProtEvPrestDes").text = evento.protocolo_evento From 9cefedef6741375dc4b6c17499dabb51b1102b47 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 11:08:00 -0300 Subject: [PATCH 024/175] fix(main): fix --- pynfe/processamento/comunicacao.py | 15 +++------------ pynfe/processamento/serializacao.py | 6 +++--- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index e38d82a8..d0de12ad 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1344,7 +1344,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, + nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, ) # if self._header: # cabecalho = self._cabecalho_soap(metodo) @@ -1352,17 +1352,8 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): # c.append(cabecalho) body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) - # distribuição tem um corpo de xml diferente - if metodo == "CTeDistribuicaoDFe": - x = etree.SubElement( - body, "cteDistDFeInteresse", xmlns=NAMESPACE_CTE_METODO + metodo - ) - a = etree.SubElement(x, "cteDadosMsg") - else: - a = etree.SubElement( - body, "cteDadosMsg", xmlns=NAMESPACE_CTE_METODO + metodo - ) - a.append(dados) + cte_dados_msg = etree.SubElement(body, "cteDadosMsg", xmlns=NAMESPACE_CTE_METODO + metodo) + cte_dados_msg.append(dados) return raiz def _post_header(self): diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 999394fd..15f1848b 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2019,7 +2019,7 @@ def serializar_evento_mdfe( def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=False): tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - raiz = etree.Element(tag_raiz, versao="4.00", nsmap={None: NAMESPACE_CTE, "ds": NAMESPACE_SIG}) + raiz = etree.Element(tag_raiz, versao="4.00", nsmap={None: NAMESPACE_CTE}) e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) @@ -2033,7 +2033,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal ) etree.SubElement(e, "tpEvento").text = evento.tp_evento etree.SubElement(e, "nSeqEvento").text = str(evento.n_seq_evento) - det = etree.SubElement(e, "detEvento", versaoEvento="4.00", nsmap={None:NAMESPACE_CTE }) + det = etree.SubElement(e, "detEvento", versaoEvento="4.00") # EVENTOS COMENTADOS NÂO TESTADOS # if evento.descricao == "Comprovante de Entrega do CT-e": # etree.Subelement(det, "nProt").text = evento.protocolo @@ -2081,7 +2081,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal etree.SubElement(cancelamento, "nProt").text = evento.protocolo etree.SubElement(cancelamento, "nProtIE").text = evento.protocolo_evento elif evento.descricao == "Prestação do Serviço em Desacordo": - desacordo = etree.SubElement(det, "evPrestDesacordo", nsmap={None:NAMESPACE_CTE}) + desacordo = etree.SubElement(det, "evPrestDesacordo") etree.SubElement(desacordo, "descEvento").text = evento.descricao etree.SubElement(desacordo, "indDesacordoOper").text = "1" #Indicador de operação em desacordo etree.SubElement(desacordo, "xObs").text = evento.observacao From 0ff50493dfe011697b1c453ba027394df01da6e8 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 12:16:50 -0300 Subject: [PATCH 025/175] fix(main): comment soap namespace --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index d0de12ad..dacef75a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1344,7 +1344,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, + # nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, ) # if self._header: # cabecalho = self._cabecalho_soap(metodo) From 15d0940e78f0c7d6e61cb7ba021f1a3cb08cd6e2 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 12:45:48 -0300 Subject: [PATCH 026/175] main(fix): fix --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index dacef75a..54e6f9bf 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1344,7 +1344,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - # nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, + nsmap={"soap12": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, ) # if self._header: # cabecalho = self._cabecalho_soap(metodo) From fdafa162adf53f722d9f229413fbc71abbeff7aa Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:22:09 -0300 Subject: [PATCH 027/175] fix(main): id fixed --- pynfe/entidades/evento.py | 13 +++++++++++++ pynfe/processamento/serializacao.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py index bfd100f9..d75d4adf 100644 --- a/pynfe/entidades/evento.py +++ b/pynfe/entidades/evento.py @@ -44,6 +44,19 @@ def identificador(self): "n_seq_evento": str(self.n_seq_evento).zfill(2), } return self.id + + def identificador_cte(self): + """ + Gera o valor para o campo id + A regra de formação do Id é: “ID” + tpEvento + chave da NF-e + nSeqEvento + O n_seq_evento pra eventos de cte tem 3 digitos + """ + self.id = "ID%(tp_evento)s%(chave)s%(n_seq_evento)s" % { + "tp_evento": self.tp_evento, + "chave": self.chave, + "n_seq_evento": str(self.n_seq_evento).zfill(3), + } + return self.id class EventoCancelarNota(Evento): diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 15f1848b..95cd329e 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2020,7 +2020,7 @@ def serializar_evento_cte(self, evento, tag_raiz="eventoCTe", retorna_string=Fal tz = datetime.now().astimezone().strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) raiz = etree.Element(tag_raiz, versao="4.00", nsmap={None: NAMESPACE_CTE}) - e = etree.SubElement(raiz, "infEvento", Id=evento.identificador) + e = etree.SubElement(raiz, "infEvento", Id=evento.identificador_cte) etree.SubElement(e, "cOrgao").text = CODIGOS_ESTADOS[evento.uf.upper()] etree.SubElement(e, "tpAmb").text = str(self._ambiente) if len(so_numeros(evento.cnpj)) == 11: From 02bd42b2875aa6c43df028fba6cff7e85f5e31b7 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:23:04 -0300 Subject: [PATCH 028/175] fix(main): property of id --- pynfe/entidades/evento.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py index d75d4adf..6df24b67 100644 --- a/pynfe/entidades/evento.py +++ b/pynfe/entidades/evento.py @@ -45,6 +45,7 @@ def identificador(self): } return self.id + @property def identificador_cte(self): """ Gera o valor para o campo id From a62cf713d591644a79099d2b1200fa3149d009de Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:34:36 -0300 Subject: [PATCH 029/175] fix(main): fix namespace --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 54e6f9bf..d0de12ad 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1344,7 +1344,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"soap12": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, + nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, ) # if self._header: # cabecalho = self._cabecalho_soap(metodo) From 96d2c6b45cf47c1af3493428c65738f106d45963 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:36:10 -0300 Subject: [PATCH 030/175] feat(main): added webservices url to eventos --- pynfe/utils/webservices.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 91f2f0f0..0f041f42 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -551,26 +551,31 @@ "HOMOLOGACAO": "https://hom1", }, "MT": { + "EVENTOS": "sefaz.mt.gov.br/ctews2/services/CTeRecepcaoEventoV4?wsdl" "STATUS": "sefaz.mt.gov.br/ctews/services/CteStatusServico", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://homologacao.", }, "MS": { + "EVENTOS": "cte.ms.gov.br/ws/CTeRecepcaoEventoV4", "STATUS": "cte.ms.gov.br/ws/CteStatusServico", "HTTPS": "https://producao.", "HOMOLOGACAO": "https://homologacao.", }, "MG": { + "EVENTOS": "fazenda.mg.gov.br/cte/services/CTeRecepcaoEventoV4", "STATUS": "fazenda.mg.gov.br/cte/services/CteStatusServico", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://hcte.", }, "PR": { + "EVENTOS": "fazenda.pr.gov.br/cte4/CTeRecepcaoEventoV4?wsdl", "STATUS": "fazenda.pr.gov.br/cte/CteStatusServico?wsdl", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://homologacao.", }, "RS": { + "EVENTOS": "svrs.rs.gov.br/ws/CTeRecepcaoEventoV4/CTeRecepcaoEventoV4.asmx", "STATUS": "svrs.rs.gov.br/ws/ctestatusservico/CteStatusServico.asmx", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://cte-homologacao.", @@ -582,11 +587,13 @@ "HOMOLOGACAO": "https://homologacao.nfe.", }, "SVRS": { + "EVENTOS": "svrs.rs.gov.br/ws/CTeRecepcaoEventoV4/CTeRecepcaoEventoV4.asmx", "STATUS": "svrs.rs.gov.br/ws/ctestatusservico/CteStatusServico.asmx", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://cte-homologacao.", }, "SVSP": { + "EVENTOS": "fazenda.sp.gov.br/CTeWS/WS/CTeRecepcaoEventoV4.asmx", "STATUS": "fazenda.sp.gov.br/cteWEB/services/CteStatusServico.asmx", "HTTPS": "https://nfe.", "HOMOLOGACAO": "https://homologacao.nfe.", From e79a9741e57fae9e616c7e41e21e0ce015f1b149 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:45:21 -0300 Subject: [PATCH 031/175] fix(main): added comma --- pynfe/utils/webservices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 0f041f42..17e96716 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -551,7 +551,7 @@ "HOMOLOGACAO": "https://hom1", }, "MT": { - "EVENTOS": "sefaz.mt.gov.br/ctews2/services/CTeRecepcaoEventoV4?wsdl" + "EVENTOS": "sefaz.mt.gov.br/ctews2/services/CTeRecepcaoEventoV4?wsdl", "STATUS": "sefaz.mt.gov.br/ctews/services/CteStatusServico", "HTTPS": "https://cte.", "HOMOLOGACAO": "https://homologacao.", From 60f3a2076ae3fe16d5a3892cb48034287a6336c5 Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:45:28 -0300 Subject: [PATCH 032/175] fix(main): print url for debbug --- pynfe/processamento/comunicacao.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index d0de12ad..3740cec1 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1333,6 +1333,7 @@ def evento(self, evento): # url do serviço url = self._get_url("EVENTOS") + print(url) # Monta XML do corpo da requisição xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) From 9b908e57c68aedd44782f80685c785a0b34c403c Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:57:55 -0300 Subject: [PATCH 033/175] fix(main): print uf to debug --- pynfe/processamento/comunicacao.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 3740cec1..20ca2175 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1269,6 +1269,7 @@ def _get_url(self, consulta): # Estados que implementam webservices proprios lista = ["MT", "MS", "MG", "PR", "RS", "SP"] + print(self.uf.upper()) if self.uf.upper() in lista: if self._ambiente == 1: ambiente = "HTTPS" From c0e0abec003fa351adbfdc61ba76d22376213ffc Mon Sep 17 00:00:00 2001 From: Hanna Tiharu <129420244+HannaTiharu@users.noreply.github.com> Date: Sat, 22 Feb 2025 15:26:03 -0300 Subject: [PATCH 034/175] fix(main): removed debugger prints --- pynfe/processamento/comunicacao.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 20ca2175..7edc7578 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1269,7 +1269,6 @@ def _get_url(self, consulta): # Estados que implementam webservices proprios lista = ["MT", "MS", "MG", "PR", "RS", "SP"] - print(self.uf.upper()) if self.uf.upper() in lista: if self._ambiente == 1: ambiente = "HTTPS" @@ -1334,7 +1333,6 @@ def evento(self, evento): # url do serviço url = self._get_url("EVENTOS") - print(url) # Monta XML do corpo da requisição xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) @@ -1387,7 +1385,6 @@ def _post(self, url, xml): etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml - print(xml) # Faz o request com o servidor result = requests.post( From 6dcabb996e7449f6ef23429cd8363f89c77ccd42 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Thu, 13 Mar 2025 11:57:09 -0300 Subject: [PATCH 035/175] fix: resolver bug --- pynfe/processamento/comunicacao.py | 230 +++++++++++------------------ 1 file changed, 88 insertions(+), 142 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 20ca2175..5e585bbf 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -3,7 +3,6 @@ import re import requests - from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils import etree, so_numeros from pynfe.utils.flags import ( @@ -16,7 +15,6 @@ NAMESPACE_MDFE_METODO, NAMESPACE_METODO, NAMESPACE_NFE, - NAMESPACE_SIG, NAMESPACE_SOAP, NAMESPACE_XSD, NAMESPACE_XSI, @@ -103,17 +101,11 @@ def autorizacao( # Estados como GO vem com a tag header inf_prot = prot[1][0] - lote_status = inf_prot.xpath( - "ns:retEnviNFe/ns:cStat", namespaces=ns - )[0].text + lote_status = inf_prot.xpath("ns:retEnviNFe/ns:cStat", namespaces=ns)[0].text # Lote processado if lote_status == "104": - prot_nfe = inf_prot.xpath( - "ns:retEnviNFe/ns:protNFe", namespaces=ns - )[0] - status = prot_nfe.xpath("ns:infProt/ns:cStat", namespaces=ns)[ - 0 - ].text + prot_nfe = inf_prot.xpath("ns:retEnviNFe/ns:protNFe", namespaces=ns)[0] + status = prot_nfe.xpath("ns:infProt/ns:cStat", namespaces=ns)[0].text # autorizado usa da NF-e # retorna xml final (protNFe+NFe) if status in ["100", "150"]: @@ -132,9 +124,7 @@ def autorizacao( status = rec.xpath("ns:retEnviNFe/ns:cStat", namespaces=ns)[0].text # Lote Recebido com Sucesso! if status == "103": - nrec = rec.xpath("ns:retEnviNFe/ns:infRec/ns:nRec", namespaces=ns)[ - 0 - ].text + nrec = rec.xpath("ns:retEnviNFe/ns:infRec/ns:nRec", namespaces=ns)[0].text return 0, nrec, nota_fiscal return 1, retorno, nota_fiscal @@ -239,7 +229,7 @@ def consulta_distribuicao( return self._post(url, xml) - def consulta_cadastro(self, modelo, documento, tipo='CNPJ', uf=None): + def consulta_cadastro(self, modelo, documento, tipo="CNPJ", uf=None): """ Consulta de cadastro :param modelo: Modelo da nota @@ -249,10 +239,24 @@ def consulta_cadastro(self, modelo, documento, tipo='CNPJ', uf=None): :return: """ # UF que utilizam a SVRS - Sefaz Virtual do RS: - lista_svrs = ["AC", "AL", "AP", "CE", - "DF", "ES", "PA", "PB", - "PI", "RJ", "RN", "RO", - "RR", "SC", "SE", "TO"] + lista_svrs = [ + "AC", + "AL", + "AP", + "CE", + "DF", + "ES", + "PA", + "PB", + "PI", + "RJ", + "RN", + "RO", + "RR", + "SC", + "SE", + "TO", + ] # Se não informada UF nos parâmetros da função, # utiliza a UF do construtor @@ -275,10 +279,10 @@ def consulta_cadastro(self, modelo, documento, tipo='CNPJ', uf=None): info = etree.SubElement(raiz, "infCons") etree.SubElement(info, "xServ").text = "CONS-CAD" etree.SubElement(info, "UF").text = uf.upper() - + # Monta tipo de documento CNPJ, CPF ou IE etree.SubElement(info, tipo.upper()).text = documento - + # etree.SubElement(info, 'CPF').text = cpf # Monta XML para envio da requisição @@ -366,18 +370,15 @@ def inutilizacao( # Identificador da TAG a ser assinada formada com Código da UF + Ano (2 posições) + # CNPJ + modelo + série + nro inicial e nro final precedida do literal “ID” - id_unico = ( - "ID%(uf)s%(ano)s%(cnpj)s%(modelo)s%(serie)s%(num_ini)s%(num_fin)s" - % { - "uf": uf, - "ano": ano, - "cnpj": cnpjcpf_chaveacesso, - "modelo": "55" if modelo == "nfe" else "65", # 55=NF-e; 65=NFC-e; - "serie": str(serie).zfill(3), - "num_ini": str(numero_inicial).zfill(9), - "num_fin": str(numero_final).zfill(9), - } - ) + id_unico = "ID%(uf)s%(ano)s%(cnpj)s%(modelo)s%(serie)s%(num_ini)s%(num_fin)s" % { + "uf": uf, + "ano": ano, + "cnpj": cnpjcpf_chaveacesso, + "modelo": "55" if modelo == "nfe" else "65", # 55=NF-e; 65=NFC-e; + "serie": str(serie).zfill(3), + "num_ini": str(numero_inicial).zfill(9), + "num_fin": str(numero_final).zfill(9), + } # Monta XML do corpo da requisição # FIXME raiz = etree.Element("inutNFe", versao=VERSAO_PADRAO, xmlns=NAMESPACE_NFE) @@ -458,9 +459,7 @@ def _get_url(self, modelo, consulta, contingencia=False): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] else: - raise Exception( - 'Modelo não encontrado! Defina modelo="nfe" ou "nfce"' - ) + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') elif self.uf.upper() in contingencia_svan: if self._ambiente == 1: ambiente = "HTTPS" @@ -473,9 +472,7 @@ def _get_url(self, modelo, consulta, contingencia=False): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] else: - raise Exception( - 'Modelo não encontrado! Defina modelo="nfe" ou "nfce"' - ) + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') return self.url # estado que implementam webservices proprios @@ -491,19 +488,14 @@ def _get_url(self, modelo, consulta, contingencia=False): self.url = NFE["SVRS"][ambiente] + NFE["SVRS"][consulta] else: # nfe Ex: https://nfe.fazenda.pr.gov.br/nfe/NFeStatusServico3 - self.url = ( - NFE[self.uf.upper()][ambiente] + NFE[self.uf.upper()][consulta] - ) + self.url = NFE[self.uf.upper()][ambiente] + NFE[self.uf.upper()][consulta] elif modelo == "nfce": # PE e BA são as únicas UF'sque possuem NFE proprio e SVRS para NFCe if self.uf.upper() == "PE" or self.uf.upper() == "BA": self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] else: # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 - self.url = ( - NFCE[self.uf.upper()][ambiente] - + NFCE[self.uf.upper()][consulta] - ) + self.url = NFCE[self.uf.upper()][ambiente] + NFCE[self.uf.upper()][consulta] else: raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') # Estados que utilizam outros ambientes @@ -537,9 +529,7 @@ def _get_url(self, modelo, consulta, contingencia=False): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] else: - raise Exception( - 'Modelo não encontrado! Defina modelo="nfe" ou "nfce"' - ) + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') # unico UF que utiliza SVAN ainda para NF-e # SVRS para NFC-e elif self.uf.upper() == "MA": @@ -554,13 +544,9 @@ def _get_url(self, modelo, consulta, contingencia=False): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] else: - raise Exception( - 'Modelo não encontrado! Defina modelo="nfe" ou "nfce"' - ) + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') else: - raise Exception( - f"Url não encontrada para {modelo} e {consulta} {self.uf.upper()}" - ) + raise Exception(f"Url não encontrada para {modelo} e {consulta} {self.uf.upper()}") return self.url def _construir_xml_soap(self, metodo, dados, cabecalho=False): @@ -572,14 +558,10 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) # distribuição tem um corpo de xml diferente if metodo == "NFeDistribuicaoDFe": - x = etree.SubElement( - body, "nfeDistDFeInteresse", xmlns=NAMESPACE_METODO + metodo - ) + x = etree.SubElement(body, "nfeDistDFeInteresse", xmlns=NAMESPACE_METODO + metodo) a = etree.SubElement(x, "nfeDadosMsg") elif metodo == "CadConsultaCadastro4" and self.uf.upper() == "MT": - x = etree.SubElement( - body, "consultaCadastro", xmlns=NAMESPACE_METODO + metodo - ) + x = etree.SubElement(body, "consultaCadastro", xmlns=NAMESPACE_METODO + metodo) a = etree.SubElement(x, "nfeDadosMsg") else: a = etree.SubElement(body, "nfeDadosMsg", xmlns=NAMESPACE_METODO + metodo) @@ -599,9 +581,7 @@ def _post_header(self): def _post(self, url, xml, timeout=None): certificado_a1 = CertificadoA1(self.certificado) - chave, cert = certificado_a1.separar_arquivo( - self.certificado_senha, caminho=True - ) + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) chave_cert = (cert, chave) # Abre a conexão HTTPS try: @@ -610,10 +590,7 @@ def _post(self, url, xml, timeout=None): # limpa xml com caracteres bugados para infNFeSupl em NFC-e xml = re.sub( "(.*?)", - lambda x: x.group(0) - .replace("<", "<") - .replace(">", ">") - .replace("&", ""), + lambda x: x.group(0).replace("<", "<").replace(">", ">").replace("&", ""), etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml @@ -756,9 +733,9 @@ def _cabecalho(self, retorna_string=True): etree.SubElement(raiz, "versaoDados").text = self._versao if retorna_string: - cabecalho = etree.tostring( - raiz, encoding="unicode", pretty_print=False - ).replace("\n", "") + cabecalho = etree.tostring(raiz, encoding="unicode", pretty_print=False).replace( + "\n", "" + ) cabecalho = xml_declaration + cabecalho return cabecalho else: @@ -775,9 +752,9 @@ def _cabecalho2(self, retorna_string=True): etree.SubElement(raiz, "versaoDados").text = self._versao if retorna_string: - cabecalho = etree.tostring( - raiz, encoding="unicode", pretty_print=False - ).replace("\n", "") + cabecalho = etree.tostring(raiz, encoding="unicode", pretty_print=False).replace( + "\n", "" + ) cabecalho = xml_declaration + cabecalho return cabecalho else: @@ -831,18 +808,13 @@ def _post_https(self, url, xml, metodo): cabecalho = self._cabecalho() # comunicacao wsdl try: - from suds.client import Client - from pynfe.utils.https_nfse import HttpAuthenticated + from suds.client import Client certificadoA1 = CertificadoA1(self.certificado) - chave, cert = certificadoA1.separar_arquivo( - self.certificado_senha, caminho=True - ) + chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) - cliente = Client( - url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url) - ) + cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) # gerar nfse if metodo == "gerar": @@ -949,24 +921,16 @@ def autorizacao(self, manifesto, id_lote=1, ind_sinc=1): try: # Protocolo com envio OK inf_prot = prot[1][0] - lote_status = inf_prot.xpath( - "ns:retEnviMDFe/ns:cStat", namespaces=ns - )[0].text + lote_status = inf_prot.xpath("ns:retEnviMDFe/ns:cStat", namespaces=ns)[0].text # Lote processado if lote_status == self._edoc_situacao_lote_processado: - prot_mdfe = inf_prot.xpath( - "ns:retEnviMDFe/ns:protMDFe", namespaces=ns - )[0] - status = prot_mdfe.xpath("ns:infProt/ns:cStat", namespaces=ns)[ - 0 - ].text + prot_mdfe = inf_prot.xpath("ns:retEnviMDFe/ns:protMDFe", namespaces=ns)[0] + status = prot_mdfe.xpath("ns:infProt/ns:cStat", namespaces=ns)[0].text # autorizado uso do MDF-e # retorna xml final (protMDFe + MDFe) - if ( - status in self._edoc_situacao_ja_enviado - ): # if status == '100': + if status in self._edoc_situacao_ja_enviado: # if status == '100': raiz = etree.Element( "mdfeProc", xmlns=NAMESPACE_MDFE, versao=VERSAO_MDFE ) @@ -985,18 +949,14 @@ def autorizacao(self, manifesto, id_lote=1, ind_sinc=1): self._edoc_situacao_arquivo_recebido_com_sucesso, self._edoc_situacao_em_processamento, ): - nrec = rec.xpath("ns:retEnviMDFe/ns:infRec/ns:nRec", namespaces=ns)[ - 0 - ].text + nrec = rec.xpath("ns:retEnviMDFe/ns:infRec/ns:nRec", namespaces=ns)[0].text return 0, nrec, manifesto return 1, retorno, manifesto def status_servico(self): url = self._get_url("STATUS") # Monta XML do corpo da requisição - raiz = etree.Element( - "consStatServMDFe", versao=self._versao, xmlns=NAMESPACE_MDFE - ) + raiz = etree.Element("consStatServMDFe", versao=self._versao, xmlns=NAMESPACE_MDFE) etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) etree.SubElement(raiz, "xServ").text = "STATUS" xml = self._construir_xml_soap("MDFeStatusServico", raiz) @@ -1016,9 +976,7 @@ def consulta(self, chave): def consulta_nao_encerrados(self, cpfcnpj): url = self._get_url("NAO_ENCERRADOS") # Monta XML do corpo da requisição - raiz = etree.Element( - "consMDFeNaoEnc", xmlns=NAMESPACE_MDFE, versao=self._versao - ) + raiz = etree.Element("consMDFeNaoEnc", xmlns=NAMESPACE_MDFE, versao=self._versao) etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) etree.SubElement(raiz, "xServ").text = "CONSULTAR NÃO ENCERRADOS" if len(cpfcnpj) == 11: @@ -1075,9 +1033,7 @@ def _construir_xml_soap(self, metodo, dados): body = etree.SubElement(raiz, "{%s}Body" % self._namespace_soap) - a = etree.SubElement( - body, self._envio_mensagem, xmlns=self._namespace_metodo + metodo - ) + a = etree.SubElement(body, self._envio_mensagem, xmlns=self._namespace_metodo + metodo) # if metodo == 'MDFeRecepcaoSinc': # body_base64 = base64.b16encode(a).decode() @@ -1093,9 +1049,9 @@ def _post_header(self, soap_webservice_method=False): # PE é a únca UF que exige SOAPAction no header if soap_webservice_method: - header[b"SOAPAction"] = ( - self._namespace_metodo + soap_webservice_method - ).encode("utf-8") + header[b"SOAPAction"] = (self._namespace_metodo + soap_webservice_method).encode( + "utf-8" + ) if self._accept: header[b"Accept"] = b"application/soap+xml; charset=utf-8;" @@ -1104,9 +1060,7 @@ def _post_header(self, soap_webservice_method=False): def _post(self, url, xml): certificado_a1 = CertificadoA1(self.certificado) - chave, cert = certificado_a1.separar_arquivo( - self.certificado_senha, caminho=True - ) + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) chave_cert = (cert, chave) # Abre a conexão HTTPS try: @@ -1115,16 +1069,11 @@ def _post(self, url, xml): # limpa xml com caracteres bugados para infMDFeSupl em NFC-e xml = re.sub( "(.*?)", - lambda x: x.group(0) - .replace("<", "<") - .replace(">", ">") - .replace("amp;", ""), + lambda x: x.group(0).replace("<", "<").replace(">", ">").replace("amp;", ""), etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml - xml = xml.encode( - "utf8" - ) # necessário para o evento "CONSULTAR NÃO ENCERRADOS" + xml = xml.encode("utf8") # necessário para o evento "CONSULTAR NÃO ENCERRADOS" print(xml) print("-" * 20) @@ -1189,15 +1138,15 @@ def status_servico(self): """ url = self._get_url("STATUS") # Monta XML do corpo da requisição - raiz = etree.Element( - "consStatServCte", versao=self._versao, xmlns=NAMESPACE_CTE - ) + raiz = etree.Element("consStatServCte", versao=self._versao, xmlns=NAMESPACE_CTE) etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) etree.SubElement(raiz, "xServ").text = "STATUS" xml = self._construir_xml_soap("CteStatusServico", raiz) return self._post(url, xml) - def consulta_distribuicao(self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False): + def consulta_distribuicao( + self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False + ): """ O XML do pedido de distribuição suporta três tipos de consultas que são definidas de acordo com a tag informada no XML. @@ -1261,7 +1210,7 @@ def _cabecalho_soap(self, metodo): raiz = etree.Element(self._header, xmlns=self._namespace_metodo + metodo) etree.SubElement(raiz, "cUF").text = CODIGOS_ESTADOS[self.uf.upper()] - etree.SubElement(raiz, "versaoDados").text = "4.00" + etree.SubElement(raiz, "versaoDados").text = "3.00" return raiz def _get_url(self, consulta): @@ -1318,7 +1267,7 @@ def _get_url(self, consulta): else: raise Exception(f"Url não encontrada para {consulta} {self.uf.upper()}") return self.url - + def evento(self, evento): """ Envia eventos do CTe como: @@ -1334,12 +1283,10 @@ def evento(self, evento): # url do serviço url = self._get_url("EVENTOS") - print(url) # Monta XML do corpo da requisição xml = self._construir_xml_soap("CTeRecepcaoEventoV4", evento) return self._post(url, xml) - def _construir_xml_soap(self, metodo, dados, cabecalho=False): """Monta o XML para o envio via SOAP""" @@ -1348,14 +1295,20 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): "{%s}Envelope" % NAMESPACE_SOAP, nsmap={"soap": NAMESPACE_SOAP, "xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD}, ) - # if self._header: - # cabecalho = self._cabecalho_soap(metodo) - # c = etree.SubElement(raiz, "{%s}Header" % self._namespace_soap) - # c.append(cabecalho) + if self._header and metodo != "CTeRecepcaoEventoV4": + cabecalho = self._cabecalho_soap(metodo) + c = etree.SubElement(raiz, "{%s}Header" % self._namespace_soap) + c.append(cabecalho) body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) - cte_dados_msg = etree.SubElement(body, "cteDadosMsg", xmlns=NAMESPACE_CTE_METODO + metodo) - cte_dados_msg.append(dados) + + # distribuição tem um corpo de xml diferente + if metodo == "CTeDistribuicaoDFe": + x = etree.SubElement(body, "cteDistDFeInteresse", xmlns=NAMESPACE_CTE_METODO + metodo) + a = etree.SubElement(x, "cteDadosMsg") + else: + a = etree.SubElement(body, "cteDadosMsg", xmlns=NAMESPACE_CTE_METODO + metodo) + a.append(dados) return raiz def _post_header(self): @@ -1369,9 +1322,7 @@ def _post_header(self): def _post(self, url, xml): certificado_a1 = CertificadoA1(self.certificado) - chave, cert = certificado_a1.separar_arquivo( - self.certificado_senha, caminho=True - ) + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) chave_cert = (cert, chave) # Abre a conexão HTTPS try: @@ -1380,10 +1331,7 @@ def _post(self, url, xml): # limpa xml com caracteres bugados para infNFeSupl em NFC-e xml = re.sub( "(.*?)", - lambda x: x.group(0) - .replace("<", "<") - .replace(">", ">") - .replace("&", ""), + lambda x: x.group(0).replace("<", "<").replace(">", ">").replace("&", ""), etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml @@ -1406,5 +1354,3 @@ def _post(self, url, xml): raise e finally: certificado_a1.excluir() - - From 9e82086739061dcda2bf2f14f89b920beab29b9c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 23 Aug 2025 22:35:24 -0300 Subject: [PATCH 036/175] feat: Refactor code structure for improved readability and maintainability --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 1092 ++++++++++++++++ .../data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd | 1097 +++++++++++++++++ pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd | 176 ++- pynfe/utils/flags.py | 29 +- 4 files changed, 2366 insertions(+), 28 deletions(-) create mode 100644 pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json create mode 100644 pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json new file mode 100644 index 00000000..44b0c686 --- /dev/null +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -0,0 +1,1092 @@ +{ + "versaoManual": "NFe/infNFe/@versao", + "idIntegracao": "NFe/infNFe/@Id", + "estado": "NFe/infNFe/ide/cUF", + "codigo": "NFe/infNFe/ide/cNF", + "natureza": "NFe/infNFe/ide/natOp", + "modelo": "NFe/infNFe/ide/mod", + "serie": "NFe/infNFe/ide/serie", + "numero": "NFe/infNFe/ide/nNF", + "dataEmissao": "NFe/infNFe/ide/dhEmi", + "dataSaidaEntradaTz": "NFe/infNFe/ide/dhSaiEnt", + "saida": "NFe/infNFe/ide/tpNF", + "codigoIdentificacaoDestino": "NFe/infNFe/ide/idDest", + "codigoMunicipioFatoGerador": "NFe/infNFe/ide/cMunFG", + "codigoMunicipioFatoGeradorIBS": "NFe/infNFe/ide/cMunFGIBS", + "tipoImpressao": "NFe/infNFe/ide/tpImp", + "tipoEmissao": "NFe/infNFe/ide/tpEmis", + "digitoVerificador": "NFe/infNFe/ide/cDV", + "ambiente": "NFe/infNFe/ide/tpAmb", + "finalidade": "NFe/infNFe/ide/finNFe", + "tipoNotaDebito": "NFe/infNFe/ide/tpNFDebito", + "tipoNotaCredito": "NFe/infNFe/ide/tpNFCredito", + "consumidorFinal": "NFe/infNFe/ide/indFinal", + "presencial": "NFe/infNFe/ide/indPres", + "intermediador": "NFe/infNFe/ide/indIntermed", + "processoEmissao": "NFe/infNFe/ide/procEmi", + "versaoProcessoEmissao": "NFe/infNFe/ide/verProc", + "dataContingencia": "NFe/infNFe/ide/dhCont", + "justificativaContingencia": "NFe/infNFe/ide/xJust", + "compraGovernamental": { + "tipoEnte": "NFe/infNFe/ide/gCompraGov/tpEnteGov", + "percentual": "NFe/infNFe/ide/gCompraGov/pRedutor", + "tipoOperacao": "NFe/infNFe/ide/gCompraGov/tpOperGov" + }, + "pagAntecipado": [{ + "chave": "NFe/infNFe/ide/gPagAntecipado/refNFe" + }], + "emitente": { + "cpfCnpj": "NFe/infNFe/emit/CNPJ", + "razaoSocial": "NFe/infNFe/emit/xNome", + "nomeFantasia": "NFe/infNFe/emit/xFant", + "endereco": { + "logradouro": "NFe/infNFe/emit/enderEmit/xLgr", + "numero": "NFe/infNFe/emit/enderEmit/nro", + "complemento": "NFe/infNFe/emit/enderEmit/xCpl", + "bairro": "NFe/infNFe/emit/enderEmit/xBairro", + "codigoCidade": "NFe/infNFe/emit/enderEmit/cMun", + "descricaoCidade": "NFe/infNFe/emit/enderEmit/xMun", + "estado": "NFe/infNFe/emit/enderEmit/UF", + "cep": "NFe/infNFe/emit/enderEmit/CEP", + "codigoPais": "NFe/infNFe/emit/enderEmit/cPais", + "descricaoPais": "NFe/infNFe/emit/enderEmit/xPais" + }, + "telefone": "NFe/infNFe/emit/enderEmit/fone", + "inscricaoEstadual": "NFe/infNFe/emit/IE", + "inscricaoEstadualSubstituto": "NFe/infNFe/emit/IEST", + "inscricaoMunicipal": "NFe/infNFe/emit/IM", + "cnae": "NFe/infNFe/emit/CNAE", + "regimeTributario": "NFe/infNFe/emit/CRT" + }, + "avulsa": { + "cnpj": "NFe/infNFe/avulsa/CNPJ", + "orgao": "NFe/infNFe/avulsa/xOrgao", + "matricula": "NFe/infNFe/avulsa/matr", + "nomdeAgente": "NFe/infNFe/avulsa/xAgente", + "telefone": "NFe/infNFe/avulsa/fone", + "estado": "NFe/infNFe/avulsa/UF", + "numeroDocumentoArrecadacao": "NFe/infNFe/avulsa/nDAR", + "dataEmissao": "NFe/infNFe/avulsa/dEmi", + "valorTotal": "NFe/infNFe/avulsa/vDAR", + "reparticaoFiscal": "NFe/infNFe/avulsa/repEmi", + "dataPagamento": "NFe/infNFe/avulsa/dPag" + }, + "destinatario": { + "cpfCnpj": "NFe/infNFe/dest/CNPJ", + "codigoEstrangeiro": "NFe/infNFe/dest/idEstrangeiro", + "razaoSocial": "NFe/infNFe/dest/xNome", + "endereco": { + "logradouro": "NFe/infNFe/dest/enderDest/xLgr", + "numero": "NFe/infNFe/dest/enderDest/nro", + "complemento": "NFe/infNFe/dest/enderDest/xCpl", + "bairro": "NFe/infNFe/dest/enderDest/xBairro", + "codigoCidade": "NFe/infNFe/dest/enderDest/cMun", + "descricaoCidade": "NFe/infNFe/dest/enderDest/xMun", + "estado": "NFe/infNFe/dest/enderDest/UF", + "cep": "NFe/infNFe/dest/enderDest/CEP", + "codigoPais": "NFe/infNFe/dest/enderDest/cPais", + "descricaoPais": "NFe/infNFe/dest/enderDest/xPais", + "telefone": "NFe/infNFe/dest/enderDest/fone" + }, + "indicadorInscricaoEstadual": "NFe/infNFe/dest/indIEDest", + "inscricaoEstadual": "NFe/infNFe/dest/IE", + "inscricaoSuframa": "NFe/infNFe/dest/ISUF", + "inscricaoMunicipal": "NFe/infNFe/dest/IM", + "email": "NFe/infNFe/dest/email" + }, + "localRetirada": { + "cpfCnpj": "NFe/infNFe/retirada/CNPJ", + "nome": "NFe/infNFe/retirada/xNome", + "lougradouro": "NFe/infNFe/retirada/xLgr", + "numero": "NFe/infNFe/retirada/nro", + "complemento": "NFe/infNFe/retirada/xCpl", + "bairro": "NFe/infNFe/retirada/xBairro", + "codigoCidade": "NFe/infNFe/retirada/cMun", + "descricaoCidade": "NFe/infNFe/retirada/xMun", + "estado": "NFe/infNFe/retirada/UF", + "cep": "NFe/infNFe/retirada/CEP", + "codigoPais": "NFe/infNFe/retirada/cPais", + "descricaoPais": "NFe/infNFe/retirada/xPais", + "telefone": "NFe/infNFe/retirada/fone", + "email": "NFe/infNFe/retirada/email", + "inscricaoEstadual": "NFe/infNFe/retirada/IE" + }, + "localEntrega": { + "cpfCnpj": "NFe/infNFe/entrega/CNPJ", + "razaoSocial": "NFe/infNFe/entrega/xNome", + "endereco": { + "logradouro": "NFe/infNFe/entrega/xLgr", + "numero": "NFe/infNFe/entrega/nro", + "complemento": "NFe/infNFe/entrega/xCpl", + "bairro": "NFe/infNFe/entrega/xBairro", + "codigoCidade": "NFe/infNFe/entrega/cMun", + "descricaoCidade": "NFe/infNFe/entrega/xMun", + "estado": "NFe/infNFe/entrega/UF", + "cep": "NFe/infNFe/entrega/CEP", + "codigoPais": "NFe/infNFe/entrega/cPais", + "descricaoPais": "NFe/infNFe/entrega/xPais" + }, + "telefone": "NFe/infNFe/entrega/fone", + "email": "NFe/infNFe/entrega/email", + "inscricaoEstadual": "NFe/infNFe/entrega/IE" + }, + "total": { + "baseCalculoIcms": "NFe/infNFe/total/ICMSTot/vBC", + "valorIcms": "NFe/infNFe/total/ICMSTot/vICMS", + "valorIcmsDesonerado": "NFe/infNFe/total/ICMSTot/vICMSDeson", + "valorIcmsFcp": "NFe/infNFe/total/ICMSTot/vFCPUFDest", + "valorIcmsEstadoDestino": "NFe/infNFe/total/ICMSTot/vICMSUFDest", + "valorIcmsEstadoRemetente": "NFe/infNFe/total/ICMSTot/vICMSUFRemet", + "valorFcp": "NFe/infNFe/total/ICMSTot/vFCP", + "baseCalculoIcmsSt": "NFe/infNFe/total/ICMSTot/vBCST", + "valorIcmsSt": "NFe/infNFe/total/ICMSTot/vST", + "valorFcpSt": "NFe/infNFe/total/ICMSTot/vFCPST", + "valorFcpStRetido": "NFe/infNFe/total/ICMSTot/vFCPSTRet", + "baseCalculoMono": "NFe/infNFe/total/ICMSTot/qBCMono", + "valorIcmsMono": "NFe/infNFe/total/ICMSTot/vICMSMono", + "baseCalculoMonoRetencao": "NFe/infNFe/total/ICMSTot/qBCMonoReten", + "valorIcmsMonoRetencao": "NFe/infNFe/total/ICMSTot/vICMSMonoReten", + "baseCalculoMonoRetido": "NFe/infNFe/total/ICMSTot/qBCMonoRet", + "valorIcmsMonoRetido": "NFe/infNFe/total/ICMSTot/vICMSMonoRet", + "valorProdutosServicos": "NFe/infNFe/total/ICMSTot/vProd", + "valorFrete": "NFe/infNFe/total/ICMSTot/vFrete", + "valorSeguro": "NFe/infNFe/total/ICMSTot/vSeg", + "valorDesconto": "NFe/infNFe/total/ICMSTot/vDesc", + "valorIi": "NFe/infNFe/total/ICMSTot/vII", + "valorIpi": "NFe/infNFe/total/ICMSTot/vIPI", + "valorIpiDevolvido": "NFe/infNFe/total/ICMSTot/vIPIDevol", + "valorPis": "NFe/infNFe/total/ICMSTot/vPIS", + "valorCofins": "NFe/infNFe/total/ICMSTot/vCOFINS", + "valorOutros": "NFe/infNFe/total/ICMSTot/vOutro", + "valorNfe": "NFe/infNFe/total/ICMSTot/vNF", + "valorAproximadoTributos": "NFe/infNFe/total/ICMSTot/vTotTrib", + "servico": { + "valor": "NFe/infNFe/total/ISSQNtot/vServ", + "baseCalculo": "NFe/infNFe/total/ISSQNtot/vBC", + "valorIss": "NFe/infNFe/total/ISSQNtot/vISS", + "valorPis": "NFe/infNFe/total/ISSQNtot/vPIS", + "valorCofins": "NFe/infNFe/total/ISSQNtot/vCOFINS", + "dataPrestacao": "NFe/infNFe/total/ISSQNtot/dCompet", + "valorDeducao": "NFe/infNFe/total/ISSQNtot/vDeducao", + "valorOutros": "NFe/infNFe/total/ISSQNtot/vOutro", + "descontoIncondicionado": "NFe/infNFe/total/ISSQNtot/vDescIncond", + "descontoCondicionado": "NFe/infNFe/total/ISSQNtot/vDescCond", + "valorRetencaoIss": "NFe/infNFe/total/ISSQNtot/vISSRet", + "codigoRegimeEspecial": "NFe/infNFe/total/ISSQNtot/cRegTrib" + }, + "valorPisRetido": "NFe/infNFe/total/retTrib/vRetPIS", + "valorCofinsRetido": "NFe/infNFe/total/retTrib/vRetCOFINS", + "valorCsllRetido": "NFe/infNFe/total/retTrib/vRetCSLL", + "baseCalculoIrrf": "NFe/infNFe/total/retTrib/vBCIRRF", + "valorIrrfRetido": "NFe/infNFe/total/retTrib/vIRRF", + "baseCalculoRetencao": "NFe/infNFe/total/retTrib/vBCRetPrev", + "valorPrevidenciaRetido": "NFe/infNFe/total/retTrib/vRetPrev", + "baseCalculoIs": "NFe/infNFe/total/ISTot/vIS", + "baseCalculoIbsCbs": "NFe/infNFe/total/IBSCBSTot/vBCIBSCBS", + "valorDiferimentoIbsUF": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSUF/vDif", + "valorDevolucaoIbsUF": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSUF/vDevTrib", + "valorIbsUF": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSUF/vIBSUF", + "valorDiferimentoIbsMunicipio": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSMun/vDif", + "valorDevolucaoIbsMunicipio": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSMun/vDevTrib", + "valorIbsMunicipio": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSMun/vIBSMun", + "valorIbs": "NFe/infNFe/total/IBSCBSTot/gIBS/vIBS", + "valorIbsCreditoPresumindo": "NFe/infNFe/total/IBSCBSTot/gIBS/vCredPres", + "valorIbsCreditoPresumidoSupensao": "NFe/infNFe/total/IBSCBSTot/gIBS/vCredPresCondSus", + "valorCbsDiferimento": "NFe/infNFe/total/IBSCBSTot/gCBS/vDif", + "valorCbsDevolucao": "NFe/infNFe/total/IBSCBSTot/gCBS/vDevTrib", + "valorCbs": "NFe/infNFe/total/IBSCBSTot/gCBS/vCBS", + "valorCbsCreditoPresumido": "NFe/infNFe/total/IBSCBSTot/gCBS/vCredPres", + "valorCbsCreditoPresumidoSupensao": "NFe/infNFe/total/IBSCBSTot/gCBS/vCredPresCondSus", + "valorIbsMono": "NFe/infNFe/total/IBSCBSTot/gMono/vIBSMono", + "valorCbsMono": "NFe/infNFe/total/IBSCBSTot/gMono/vCBSMono", + "valorIbsMonoRetencao": "NFe/infNFe/total/IBSCBSTot/gMono/vIBSMonoReten", + "valorCbsMonoRetenencao": "NFe/infNFe/total/IBSCBSTot/gMono/vCBSMonoReten", + "valorIbsMonoRetido": "NFe/infNFe/total/IBSCBSTot/gMono/vIBSMonoRet", + "valorCbsMonoRetido": "NFe/infNFe/total/IBSCBSTot/gMono/vCBSMonoRet", + "valorIbsCbsIs": "NFe/infNFe/total/vNFTot" + }, + "transporte": { + "modalidadeFrete": "NFe/infNFe/transp/modFrete", + "transportador": { + "cpfCnpj": "NFe/infNFe/transp/transporta/CNPJ", + "nome": "NFe/infNFe/transp/transporta/xNome", + "inscricaoEstadual": "NFe/infNFe/transp/transporta/IE", + "endereco": { + "logradouro": "NFe/infNFe/transp/transporta/xEnder", + "descricaoCidade": "NFe/infNFe/transp/transporta/xMun", + "uf": "NFe/infNFe/transp/transporta/UF" + } + }, + "retencaoICMS": { + "valorServico": "NFe/infNFe/transp/retTransp/vServ", + "baseICMS": "NFe/infNFe/transp/retTransp/vBCRet", + "aliquota": "NFe/infNFe/transp/retTransp/pICMSRet", + "valorICMSRet": "NFe/infNFe/transp/retTransp/vICMSRet", + "cfop": "NFe/infNFe/transp/retTransp/CFOP", + "codigoMunicipioOcorrencia": "NFe/infNFe/transp/retTransp/cMunFG" + }, + "veiculo": { + "placa": "NFe/infNFe/transp/veicTransp/placa", + "uf": "NFe/infNFe/transp/veicTransp/UF", + "rntc": "NFe/infNFe/transp/veicTransp/RNTC" + }, + "reboque": [{ + "vagao": "NFe/infNFe/transp/vagao", + "balsa": "NFe/infNFe/transp/balsa", + "placa": "NFe/infNFe/transp/reboque/placa", + "uf": "NFe/infNFe/transp/reboque/UF", + "rntc": "NFe/infNFe/transp/reboque/RNTC" + }], + "volumes": [{ + "quantidade": "NFe/infNFe/transp/vol/qVol", + "especie": "NFe/infNFe/transp/vol/esp", + "marca": "NFe/infNFe/transp/vol/marca", + "numeracao": "NFe/infNFe/transp/vol/nVol", + "pesoLiquido": "NFe/infNFe/transp/vol/pesoL", + "pesoBruto": "NFe/infNFe/transp/vol/pesoB", + "lacres": [{ + "numero": "NFe/infNFe/transp/vol/lacres/nLacre" + }] + }] + }, + "cobranca": { + "numero": "NFe/infNFe/cobr/fat/nFat", + "valorTotal": "NFe/infNFe/cobr/fat/vOrig", + "valorDesconto": "NFe/infNFe/cobr/fat/vDesc", + "valorLiquido": "NFe/infNFe/cobr/fat/vLiq", + "parcelas": [{ + "numero": "NFe/infNFe/cobr/dup/nDup", + "dataVencimento": "NFe/infNFe/cobr/dup/dVenc", + "valor": "NFe/infNFe/cobr/dup/vDup" + }] + }, + "valorTroco": "NFe/infNFe/pag/vTroco", + "informacoesComplementaresContribuinte": "NFe/infNFe/infAdic/infAdFisco", + "informacoesComplementares": "NFe/infNFe/infAdic/infCpl", + "exportacao": { + "estadoEmbarque": "NFe/infNFe/exporta/UFSaidaPais", + "descricaoLocalEmbarque": "NFe/infNFe/exporta/xLocExporta", + "descricaoLocalDespacho": "NFe/infNFe/exporta/xLocDespacho" + }, + "compra": { + "notaEmpenho": "NFe/infNFe/compra/xNEmp", + "pedido": "NFe/infNFe/compra/xPed", + "contrato": "NFe/infNFe/compra/xCont" + }, + "cana": { + "safra": "NFe/infNFe/cana/safra", + "dataReferencia": "NFe/infNFe/cana/ref", + "fornecimentoDiario": [{ + "dia": "NFe/infNFe/cana/forDia/@dia", + "quantidadeKg": "NFe/infNFe/cana/forDia/qtde", + "quantidadeMes": "NFe/infNFe/cana/qTotMes", + "quantidadeAnterior": "NFe/infNFe/cana/qTotAnt", + "quantidadeGeral": "NFe/infNFe/cana/qTotGer" + }] + }, + "canaDeducoes": [{ + "descricao": "NFe/infNFe/cana/deduc/xDed", + "valor": "NFe/infNFe/cana/deduc/vDed", + "valorFornecimentos": "NFe/infNFe/cana/vFor", + "valorTotal": "NFe/infNFe/cana/vTotDed", + "valorLiquidoFornecimento": "NFe/infNFe/cana/vLiqFor" + }], + "nfeReferenciada": [{ + "chave": "NFe/infNFe/ide/NFref/refNFe", + "chaveSigilo": "NFe/infNFe/ide/NFref/refNFeSig", + "nfePapel": { + "estado": "NFe/infNFe/ide/NFref/refNF/cUF", + "dataEmissao": "NFe/infNFe/ide/NFref/refNF/AAMM", + "cpfCnpj": "NFe/infNFe/ide/NFref/refNF/CNPJ", + "modelo": "NFe/infNFe/ide/NFref/refNF/mod", + "serie": "NFe/infNFe/ide/NFref/refNF/serie", + "numero": "NFe/infNFe/ide/NFref/refNF/nNF" + }, + "produtorRural": { + "estado": "NFe/infNFe/ide/NFref/refNFP/cUF", + "dataEmissao": "NFe/infNFe/ide/NFref/refNFP/AAMM", + "cnpj": "NFe/infNFe/ide/NFref/refNFP/CNPJ", + "cpf": "NFe/infNFe/ide/NFref/refNFP/CPF", + "inscricaoEstadual": "NFe/infNFe/ide/NFref/refNFP/IE", + "modelo": "NFe/infNFe/ide/NFref/refNFP/mod", + "serie": "NFe/infNFe/ide/NFref/refNFP/serie", + "numero": "NFe/infNFe/ide/NFref/refNFP/nNF" + }, + "chaveCte": "NFe/infNFe/ide/NFref/refCTe", + "cupomFiscal": { + "modelo": "NFe/infNFe/ide/NFref/refECF/mod", + "numeroOrdemSequencia": "NFe/infNFe/ide/NFref/refECF/nECF", + "numeroContador": "NFe/infNFe/ide/NFref/refECF/nCOO" + } + }], + "responsavelAutorizado": [{ + "cpfCnpj": "NFe/infNFe/autXML/CNPJ" + }], + "itens": [{ + "codigo": "NFe/infNFe/det/prod/cProd", + "codigoEAN": "NFe/infNFe/det/prod/cEAN", + "codigoBarras": "NFe/infNFe/det/prod/cBarra", + "descricao": "NFe/infNFe/det/prod/xProd", + "ncm": "NFe/infNFe/det/prod/NCM", + "cest": "NFe/infNFe/det/prod/CEST", + "indicadorEscalaRelevante": "NFe/infNFe/det/prod/indEscala", + "cnpjFabricante": "NFe/infNFe/det/prod/CNPJFab", + "codigoBeneficioFiscal": "NFe/infNFe/det/prod/cBenef", + "creditoPresumido": [{ + "codigo": "NFe/infNFe/det/prod/gCred/cCredPresumido", + "percentual": "NFe/infNFe/det/prod/gCred/pCredPresumido", + "valor": "NFe/infNFe/det/prod/gCred/vCredPresumido" + }], + "exTipi": "NFe/infNFe/det/prod/EXTIPI", + "cfop": "NFe/infNFe/det/prod/CFOP", + "unidade": { + "comercial": "NFe/infNFe/det/prod/uCom", + "tributavel": "NFe/infNFe/det/prod/uTrib" + }, + "quantidade": { + "comercial": "NFe/infNFe/det/prod/qCom", + "tributavel": "NFe/infNFe/det/prod/qTrib" + }, + "valorUnitario": { + "comercial": "NFe/infNFe/det/prod/vUnCom", + "tributavel": "NFe/infNFe/det/prod/vUnTrib" + }, + "valor": "NFe/infNFe/det/prod/vProd", + "codigoEANTributavel": "NFe/infNFe/det/prod/cEANTrib", + "codigoBarrasTributavel": "NFe/infNFe/det/prod/cBarraTrib", + "valorFrete": "NFe/infNFe/det/prod/vFrete", + "valorSeguro": "NFe/infNFe/det/prod/vSeg", + "valorDesconto": "NFe/infNFe/det/prod/vDesc", + "valorOutros": "NFe/infNFe/det/prod/vOutro", + "compoeTotal": "NFe/infNFe/det/prod/indTot", + "indicadorBemMovelUsado": "NFe/infNFe/det/prod/indBemMovelUsado", + "numeroCompra": "NFe/infNFe/det/prod/xPed", + "pedidoCompra": "NFe/infNFe/det/prod/nItemPed", + "numeroFci": "NFe/infNFe/det/prod/nFCI", + "veiculo": { + "tipoOperacao": "NFe/infNFe/det/prod/veicProd/tpOp", + "chassi": "NFe/infNFe/det/prod/veicProd/chassi", + "codigoCor": "NFe/infNFe/det/prod/veicProd/cCor", + "descricaoCor": "NFe/infNFe/det/prod/veicProd/xCor", + "potenciaMotor": "NFe/infNFe/det/prod/veicProd/pot", + "cilindradas": "NFe/infNFe/det/prod/veicProd/cilin", + "pesoLiquido": "NFe/infNFe/det/prod/veicProd/pesoL", + "pesoBruto": "NFe/infNFe/det/prod/veicProd/pesoB", + "numeroSerie": "NFe/infNFe/det/prod/veicProd/nSerie", + "tipoCombustivel": "NFe/infNFe/det/prod/veicProd/tpComb", + "numeroMotor": "NFe/infNFe/det/prod/veicProd/nMotor", + "capacidadeTracao": "NFe/infNFe/det/prod/veicProd/CMT", + "distanciaEixos": "NFe/infNFe/det/prod/veicProd/dist", + "anoModelo": "NFe/infNFe/det/prod/veicProd/anoMod", + "anoFabricacao": "NFe/infNFe/det/prod/veicProd/anoFab", + "tipoPintura": "NFe/infNFe/det/prod/veicProd/tpPint", + "tipo": "NFe/infNFe/det/prod/veicProd/tpVeic", + "especie": "NFe/infNFe/det/prod/veicProd/espVeic", + "condicaoVin": "NFe/infNFe/det/prod/veicProd/VIN", + "condicao": "NFe/infNFe/det/prod/veicProd/condVeic", + "codigoModelo": "NFe/infNFe/det/prod/veicProd/cMod", + "codigoCorDenatran": "NFe/infNFe/det/prod/veicProd/cCorDENATRAN", + "lotacaoMaxima": "NFe/infNFe/det/prod/veicProd/lota", + "restricao": "NFe/infNFe/det/prod/veicProd/tpRest" + }, + "medicamentos": [{ + "codigoAnvisa": "NFe/infNFe/det/prod/med/cProdANVISA", + "motivoInsencaoAnvisa": "NFe/infNFe/det/prod/med/xMotivoIsencao", + "valorMaximo": "NFe/infNFe/det/prod/med/vPMC" + }], + "combustivel": { + "codigoAnp": "NFe/infNFe/det/prod/comb/cProdANP", + "descricaoAnp": "NFe/infNFe/det/prod/comb/descANP", + "percentualGlp": "NFe/infNFe/det/prod/comb/pGLP", + "percentualGnn": "NFe/infNFe/det/prod/comb/pGNn", + "percentualGni": "NFe/infNFe/det/prod/comb/pGNi", + "valorPartida": "NFe/infNFe/det/prod/comb/vPart", + "codigoAutorizacao": "NFe/infNFe/det/prod/comb/CODIF", + "faturamentoTemperaturaAmbiente": "NFe/infNFe/det/prod/comb/qTemp", + "estadoConsumo": "NFe/infNFe/det/prod/comb/UFCons", + "percentualMistura": "NFe/infNFe/det/prod/comb/pBio", + "cide": { + "baseCalculo": "NFe/infNFe/det/prod/comb/CIDE/qBCProd", + "aliquota": "NFe/infNFe/det/prod/comb/CIDE/vAliqProd", + "valor": "NFe/infNFe/det/prod/comb/CIDE/vCIDE" + }, + "encerrante": { + "numeroBico": "NFe/infNFe/det/prod/comb/encerrante/nBico", + "numeroBomba": "NFe/infNFe/det/prod/comb/encerrante/nBomba", + "numeroTanque": "NFe/infNFe/det/prod/comb/encerrante/nTanque", + "valorInicio": "NFe/infNFe/det/prod/comb/encerrante/vEncIni", + "valorFinal": "NFe/infNFe/det/prod/comb/encerrante/vEncFin" + }, + "origemCombustivel": [{ + "indicadorImportacao": "NFe/infNFe/det/prod/comb/origComb/indImport", + "codigoUf": "NFe/infNFe/det/prod/comb/origComb/cUFOrig", + "percentualOrigUf": "NFe/infNFe/det/prod/comb/origComb/pOrig" + }] + }, + "papelImune": { + "numero": "NFe/infNFe/det/prod/nRECOPI" + }, + "tributos": { + "valorAproximadoTributos": "NFe/infNFe/det/imposto/vTotTrib", + "icms": { + "origem_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/orig", + "cst_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/CST", + "baseCalculo": { + "modalidadeDeterminacao_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/modBC", + "valor_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/vBC", + "modalidadeDeterminacao_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/modBC", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vBC", + "modalidadeDeterminacao_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/modBC", + "percentualReducao_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/pRedBC", + "valor_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/vBC", + "modalidadeDeterminacao_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/modBC", + "percentualReducao_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/pRedBC", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vBC", + "modalidadeDeterminacao_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/modBC", + "percentualReducao_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pRedBC", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vBC", + "modalidadeDeterminacao_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/modBC", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vBC", + "percentualReducao_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pRedBC", + "modalidadeDeterminacao_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/modBC", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vBC", + "percentualReducao_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pRedBC", + "modalidadeDeterminacao_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/modBC", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vBC", + "percentualReducao_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pRedBC" + }, + "aliquota_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/pICMS", + "valor_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/vICMS", + "fundoCombatePobreza": { + "baseCalculo": { + "aliquota_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/pFCP", + "valor_ICMS00": "NFe/infNFe/det/imposto/ICMS/ICMS00/vFCP", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vBCFCP", + "valor_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/vBCFCP", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vBCFCP", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vBCFCP", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vBCFCP" + }, + "aliquota_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pFCP", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vFCP", + "aliquota_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/pFCP", + "valor_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/vFCP", + "aliquota_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/pFCP", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vFCP", + "diferimento": { + "percentual_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/pFCPDif", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vFCPDif", + "valorEfetivo_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vFCPEfet" + }, + "aliquota_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pFCP", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vFCP", + "aliquota_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pFCP", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vFCP" + }, + "origem_ICMS02": "NFe/infNFe/det/imposto/ICMS/ICMS02/orig", + "cst_ICMS02": "NFe/infNFe/det/imposto/ICMS/ICMS02/CST", + "baseCalculoMono_ICMS02": "NFe/infNFe/det/imposto/ICMS/ICMS02/qBCMono", + "valorIcmsMono_ICMS02": "NFe/infNFe/det/imposto/ICMS/ICMS02/vICMSMono", + "aliquotaAdRem_ICMS02": "NFe/infNFe/det/imposto/ICMS/ICMS02/adRemICMS", + "origem_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/orig", + "cst_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/CST", + "aliquota_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pICMS", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vICMS", + "substituicaoTributaria": { + "baseCalculo": { + "modalidadeDeterminacao_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/modBCST", + "percentualReducao_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pRedBCST", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vBCST", + "modalidadeDeterminacao_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/modBCST", + "percentualReducao_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/pRedBCST", + "valor_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/vBCST", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vBCSTRet", + "modalidadeDeterminacao_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/modBCST", + "percentualReducao_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pRedBCST", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vBCST", + "modalidadeDeterminacao_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/modBCST", + "percentualReducao_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pRedBCST", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vBCST", + "modalidadeDeterminacao_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/modBCST", + "percentualReducao_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pRedBCST", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vBCST", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vBCSTRet", + "modalidadeDeterminacao_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/modBCST", + "percentualReducao_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/pRedBCST", + "valor_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/vBCST", + "modalidadeDeterminacao_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/modBCST", + "percentualReducao_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/pRedBCST", + "valor_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/vBCST", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vBCSTRet", + "modalidadeDeterminacao_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/modBCST", + "percentualReducao_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pRedBCST", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vBCST" + }, + "margemValorAdicionado": { + "percentual_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pMVAST", + "percentual_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/pMVAST", + "percentual_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pMVAST", + "percentual_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pMVAST", + "percentual_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pMVAST", + "percentual_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/pMVAST", + "percentual_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/pMVAST", + "percentual_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pMVAST" + }, + "aliquota_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pICMSST", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vICMSST", + "fundoCombatePobreza": { + "baseCalculo": { + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vBCFCPST", + "valor_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/vBCFCPST", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vBCFCPSTRet", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vBCFCPST", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vBCFCPST", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vBCFCPST", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vBCFCPSTRet", + "valor_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/vBCFCPST", + "valor_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/vBCFCPST", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vBCFCPSTRet", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vBCFCPST" + }, + "aliquota_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/pFCPST", + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vFCPST", + "aliquota_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/pFCPST", + "valor_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/vFCPST", + "aliquota_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/pFCPSTRet", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vFCPSTRet", + "aliquota_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pFCPST", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vFCPST", + "aliquota_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pFCPST", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vFCPST", + "aliquota_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pFCPST", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vFCPST", + "aliquota_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/pFCPSTRet", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vFCPSTRet", + "aliquota_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/pFCPST", + "valor_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/vFCPST", + "aliquota_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/pFCPST", + "valor_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/vFCPST", + "aliquota_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/pFCPSTRet", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vFCPSTRet", + "aliquota_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pFCPST", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vFCPST" + }, + "desoneracao": { + "valor_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/vICMSSTDeson", + "motivo_ICMS10": "NFe/infNFe/det/imposto/ICMS/ICMS10/motDesICMSST", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vICMSSTDeson", + "motivo_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/motDesICMSST", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vICMSSTDeson", + "motivo_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/motDesICMSST" + }, + "aliquota_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/pICMSST", + "valor_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/vICMSST", + "aliquota_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/pST", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vICMSSTRet", + "aliquota_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pICMSST", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vICMSST", + "aliquota_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pICMSST", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vICMSST", + "aliquota_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pICMSST", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vICMSST", + "ufDevido_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/UFST", + "aliquota_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/pST", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vICMSSTRet", + "ufDestino": { + "baseCalculo": { + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vBCSTDest" + }, + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vICMSSTDest" + }, + "aliquota_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/pICMSST", + "valor_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/vICMSST", + "aliquota_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/pICMSST", + "valor_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/vICMSST", + "aliquota_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/pST", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vICMSSTRet", + "aliquota_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pICMSST", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vICMSST" + }, + "origem_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/orig", + "cst_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/CST", + "baseCalculoMono_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/qBCMono", + "valorIcmsMono_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/vICMSMono", + "aliquotaAdRem_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/adRemICMS", + "baseCalculoMonoRetencao_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/qBCMonoReten", + "aliquotaAdRemRetencao_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/adRemICMSReten", + "valorIcmsMonoRetencao_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/vICMSMonoReten", + "percentualReducaoAdRem_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/pRedAdRem", + "motivoReducaoAdRem_ICMS15": "NFe/infNFe/det/imposto/ICMS/ICMS15/motRedAdRem", + "origem_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/orig", + "cst_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/CST", + "aliquota_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/pICMS", + "valor_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/vICMS", + "desoneracao": { + "valor_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/vICMSDeson", + "deduzItem_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/indDeduzDeson", + "motivo_ICMS20": "NFe/infNFe/det/imposto/ICMS/ICMS20/motDesICMS", + "valor_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/vICMSDeson", + "deduzItem_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/indDeduzDeson", + "motivo_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/motDesICMS", + "valor_ICMS40": "NFe/infNFe/det/imposto/ICMS/ICMS40/vICMSDeson", + "deduzItem_ICMS40": "NFe/infNFe/det/imposto/ICMS/ICMS40/indDeduzDeson", + "motivo_ICMS40": "NFe/infNFe/det/imposto/ICMS/ICMS40/motDesICMS", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vICMSDeson", + "deduzItem_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/indDeduzDeson", + "motivo_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/motDesICMS", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vICMSDeson", + "deduzItem_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/indDeduzDeson", + "motivo_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/motDesICMS" + }, + "origem_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/orig", + "cst_ICMS30": "NFe/infNFe/det/imposto/ICMS/ICMS30/CST", + "origem_ICMS40": "NFe/infNFe/det/imposto/ICMS/ICMS40/orig", + "cst_ICMS40": "NFe/infNFe/det/imposto/ICMS/ICMS40/CST", + "origem_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/orig", + "cst_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/CST", + "codigoBeneficioFiscalRBC_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/cBenefRBC", + "aliquota_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/pICMS", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vICMSOp", + "diferimento": { + "percentual_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/pDif", + "valor_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vICMSDif", + "valorIcmsDevido_ICMS51": "NFe/infNFe/det/imposto/ICMS/ICMS51/vICMS" + }, + "origem_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/orig", + "cst_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/CST", + "baseCalculoMonoDiferido_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/qBCMonoDif", + "aliquotaAdRemDiferido_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/adRemICMSDif", + "valorIcmsDiferido_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/vICMSMonoDif", + "baseCalculoMono_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/qBCMono", + "aliquotaAdRem_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/adRemICMS", + "valorIcmsMonoOperacao_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/vICMSMonoOp", + "percentualDiferimento_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/pDif", + "valorIcmsMono_ICMS53": "NFe/infNFe/det/imposto/ICMS/ICMS53/vICMSMono", + "origem_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/orig", + "cst_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/CST", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vICMSSubstituto", + "efetivo": { + "baseCalculo": { + "percentualReducao_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/pRedBCEfet", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vBCEfet", + "percentualReducao_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/pRedBCEfet", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vBCEfet", + "percentualReducao_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/pRedBCEfet", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vBCEfet" + }, + "aliquota_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/pICMSEfet", + "valor_ICMS60": "NFe/infNFe/det/imposto/ICMS/ICMS60/vICMSEfet", + "aliquota_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/pICMSEfet", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vICMSEfet", + "aliquota_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/pICMSEfet", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vICMSEfet" + }, + "origem_ICMS61": "NFe/infNFe/det/imposto/ICMS/ICMS61/orig", + "cst_ICMS61": "NFe/infNFe/det/imposto/ICMS/ICMS61/CST", + "baseCalculoMonoRetido_ICMS61": "NFe/infNFe/det/imposto/ICMS/ICMS61/qBCMonoRet", + "aliquotaAdRemRetido_ICMS61": "NFe/infNFe/det/imposto/ICMS/ICMS61/adRemICMSRet", + "valorIcmsMonoRetido_ICMS61": "NFe/infNFe/det/imposto/ICMS/ICMS61/vICMSMonoRet", + "origem_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/orig", + "cst_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/CST", + "aliquota_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/pICMS", + "valor_ICMS70": "NFe/infNFe/det/imposto/ICMS/ICMS70/vICMS", + "origem_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/orig", + "cst_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/CST", + "aliquota_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/pICMS", + "valor_ICMS90": "NFe/infNFe/det/imposto/ICMS/ICMS90/vICMS", + "origem_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/orig", + "cst_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/CST", + "aliquota_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pICMS", + "valor_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/vICMS", + "operacaoPropria": { + "baseCalculo": { + "percentual_ICMSPart": "NFe/infNFe/det/imposto/ICMS/ICMSPart/pBCOp" + } + }, + "origem_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/orig", + "cst_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/CST", + "valor_ICMSST": "NFe/infNFe/det/imposto/ICMS/ICMSST/vICMSSubstituto", + "origem_ICMSSN101": "NFe/infNFe/det/imposto/ICMS/ICMSSN101/orig", + "csosn_ICMSSN101": "NFe/infNFe/det/imposto/ICMS/ICMSSN101/CSOSN", + "creditoSimplesNacional": { + "percentual_ICMSSN101": "NFe/infNFe/det/imposto/ICMS/ICMSSN101/pCredSN", + "valor_ICMSSN101": "NFe/infNFe/det/imposto/ICMS/ICMSSN101/vCredICMSSN", + "percentual_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/pCredSN", + "valor_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/vCredICMSSN", + "percentual_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pCredSN", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vCredICMSSN" + }, + "origem_ICMSSN102": "NFe/infNFe/det/imposto/ICMS/ICMSSN102/orig", + "csosn_ICMSSN102": "NFe/infNFe/det/imposto/ICMS/ICMSSN102/CSOSN", + "origem_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/orig", + "csosn_ICMSSN201": "NFe/infNFe/det/imposto/ICMS/ICMSSN201/CSOSN", + "origem_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/orig", + "csosn_ICMSSN202": "NFe/infNFe/det/imposto/ICMS/ICMSSN202/CSOSN", + "origem_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/orig", + "csosn_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/CSOSN", + "valor_ICMSSN500": "NFe/infNFe/det/imposto/ICMS/ICMSSN500/vICMSSubstituto", + "origem_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/orig", + "csosn_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/CSOSN", + "aliquota_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/pICMS", + "valor_ICMSSN900": "NFe/infNFe/det/imposto/ICMS/ICMSSN900/vICMS" + }, + "is": { + "cst": "NFe/infNFe/det/imposto/IS/CSTIS", + "classificao": "NFe/infNFe/det/imposto/IS/cClassTribIS", + "baseCalculo": "NFe/infNFe/det/imposto/IS/vBCIS", + "aliquota": "NFe/infNFe/det/imposto/IS/pIS", + "aliquotaEspecifica": "NFe/infNFe/det/imposto/IS/pISEspec", + "unidade": "NFe/infNFe/det/imposto/IS/uTrib", + "quantidade": "NFe/infNFe/det/imposto/IS/qTrib", + "valor": "NFe/infNFe/det/imposto/IS/vIS" + }, + "ibscbs": { + "cst": "NFe/infNFe/det/imposto/IBSCBS/CST", + "classificacao": "NFe/infNFe/det/imposto/IBSCBS/cClassTrib", + "baseCalculo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/vBC", + "uf": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/pIBSUF", + "diferimento": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/gDif/pDif", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/gDif/vDif" + }, + "devolucao": { + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/gDevTrib/vDevTrib" + }, + "reducao": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/gRed/pRedAliq", + "aliquotaEfetiva": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/gRed/pAliqEfet" + }, + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/vIBSUF" + }, + "municipio": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/pIBSMun", + "diferimento": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/gDif/pDif", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/gDif/vDif" + }, + "devolucao": { + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/gDevTrib/vDevTrib" + }, + "reducao": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/gRed/pRedAliq", + "aliquotaEfetiva": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/gRed/pAliqEfet" + }, + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/vIBSMun" + }, + "cbs": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/pCBS", + "diferimento": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/gDif/pDif", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/gDif/vDif" + }, + "devolucao": { + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/gDevTrib/vDevTrib" + }, + "reducao": { + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/gRed/pRedAliq", + "aliquotaEfetiva": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/gRed/pAliqEfet" + }, + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/vCBS" + }, + "tributacaoRegular": { + "cts": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/CSTReg", + "classificacao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/cClassTribReg", + "aliquotaUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/pAliqEfetRegIBSUF", + "valorUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/vTribRegIBSUF", + "aliquotaMunicipio": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/pAliqEfetRegIBSMun", + "valorMunicipio": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/vTribRegIBSMun", + "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/pAliqEfetRegCBS", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/vTribRegCBS" + }, + "ibsCreditoPresumido": { + "codigo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/cCredPres", + "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/pCredPres", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPres", + "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPresCondSus" + }, + "cbsCreditoPresumido": { + "codigo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/cCredPres", + "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/pCredPres", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPres", + "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPresCondSus" + }, + "compraGovernamental": { + "aliquotaUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/pAliqIBSUF", + "valorUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/vTribIBSUF", + "aliquotaMunicipio": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/pAliqIBSMun", + "valorMunicipio": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/vTribIBSMun", + "aliquotaCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/pAliqCBS", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/vTribCBS" + }, + "monofasico": { + "quantidade": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMono", + "aliquotaAdRemIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBS", + "aliquotaAdRemCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBS", + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMono", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMono", + "quantidadeRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMonoReten", + "aliquotaAdRemIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBSReten", + "valorIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoReten", + "aliquotaAdRemCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBSReten", + "valorCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoReten", + "quantidadeRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMonoRet", + "aliquotaAdRemIbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBSRet", + "valorIbsRetido": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoRet", + "aliquotaAdRemCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBSRet", + "valorCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoRet", + "aliquotaIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/pDifIBS", + "valorIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoDif", + "aliquotaCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/pDifCBS", + "valorCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoDif", + "valorTotalIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vTotIBSMonoItem", + "valorTotalCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vTotCBSMonoItem" + }, + "transferenciaCredito": { + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gTransfCred/vIBS", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gTransfCred/vCBS" + }, + "creditoPresumidoZfm": { + "tipo": "NFe/infNFe/det/imposto/IBSCBS/gCredPresIBSZFM/tpCredPresIBSZFM", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gCredPresIBSZFM/vCredPresIBSZFM" + } + }, + "ipi": { + "cnpjProdutor": "NFe/infNFe/det/imposto/IPI/CNPJProd", + "seloControle": { + "codigo": "NFe/infNFe/det/imposto/IPI/cSelo", + "quantidade": "NFe/infNFe/det/imposto/IPI/qSelo" + }, + "codigoEnquadramentoLegal": "NFe/infNFe/det/imposto/IPI/cEnq", + "cst_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/CST", + "baseCalculo_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/vBC", + "unidade": { + "quantidade_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/qUnid", + "valor_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/vUnid" + }, + "aliquota_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/pIPI", + "valor_IPITrib": "NFe/infNFe/det/imposto/IPI/IPITrib/vIPI", + "cst_IPINT": "NFe/infNFe/det/imposto/IPI/IPINT/CST" + }, + "importacao": { + "baseCalculo": "NFe/infNFe/det/imposto/II/vBC", + "valorDespesasAduaneiras": "NFe/infNFe/det/imposto/II/vDespAdu", + "valor": "NFe/infNFe/det/imposto/II/vII", + "valorOperacoesFinanceiras": "NFe/infNFe/det/imposto/II/vIOF" + }, + "issqn": { + "baseCalculo": "NFe/infNFe/det/imposto/ISSQN/vBC", + "aliquota": "NFe/infNFe/det/imposto/ISSQN/vAliq", + "valor": "NFe/infNFe/det/imposto/ISSQN/vISSQN", + "codigoMunicipioFatoGerador": "NFe/infNFe/det/imposto/ISSQN/cMunFG", + "codigoServico": "NFe/infNFe/det/imposto/ISSQN/cListServ", + "valorDeducao": "NFe/infNFe/det/imposto/ISSQN/vDeducao", + "valorOutros": "NFe/infNFe/det/imposto/ISSQN/vOutro", + "descontoIncondicionado": "NFe/infNFe/det/imposto/ISSQN/vDescIncond", + "descontoCondicionado": "NFe/infNFe/det/imposto/ISSQN/vDescCond", + "valorRetencaoIss": "NFe/infNFe/det/imposto/ISSQN/vISSRet", + "codigoExigibilidade": "NFe/infNFe/det/imposto/ISSQN/indISS", + "codigoMunicipalServico": "NFe/infNFe/det/imposto/ISSQN/cServico", + "codigoMunicipioIncidencia": "NFe/infNFe/det/imposto/ISSQN/cMun", + "codigoPais": "NFe/infNFe/det/imposto/ISSQN/cPais", + "numeroProcessoSuspensao": "NFe/infNFe/det/imposto/ISSQN/nProcesso", + "incentivoFiscal": "NFe/infNFe/det/imposto/ISSQN/indIncentivo" + }, + "pis": { + "cst_PISAliq": "NFe/infNFe/det/imposto/PIS/PISAliq/CST", + "baseCalculo": { + "valor_PISAliq": "NFe/infNFe/det/imposto/PIS/PISAliq/vBC", + "valor_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/vBC" + }, + "aliquota_PISAliq": "NFe/infNFe/det/imposto/PIS/PISAliq/pPIS", + "valor_PISAliq": "NFe/infNFe/det/imposto/PIS/PISAliq/vPIS", + "cst_PISQtde": "NFe/infNFe/det/imposto/PIS/PISQtde/CST", + "quantidadeVendida_PISQtde": "NFe/infNFe/det/imposto/PIS/PISQtde/qBCProd", + "aliquotaReais_PISQtde": "NFe/infNFe/det/imposto/PIS/PISQtde/vAliqProd", + "valor_PISQtde": "NFe/infNFe/det/imposto/PIS/PISQtde/vPIS", + "cst_PISNT": "NFe/infNFe/det/imposto/PIS/PISNT/CST", + "cst_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/CST", + "aliquota_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/pPIS", + "quantidadeVendida_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/qBCProd", + "aliquotaReais_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/vAliqProd", + "valor_PISOutr": "NFe/infNFe/det/imposto/PIS/PISOutr/vPIS", + "substituicaoTributaria": { + "baseCalculo": "NFe/infNFe/det/imposto/PISST/vBC", + "aliquota": "NFe/infNFe/det/imposto/PISST/pPIS", + "quantidadeVendida": "NFe/infNFe/det/imposto/PISST/qBCProd", + "aliquotaReais": "NFe/infNFe/det/imposto/PISST/vAliqProd", + "valor": "NFe/infNFe/det/imposto/PISST/vPIS", + "compoeTotal": "NFe/infNFe/det/imposto/PISST/indSomaPISST" + } + }, + "cofins": { + "cst_COFINSAliq": "NFe/infNFe/det/imposto/COFINS/COFINSAliq/CST", + "baseCalculo": { + "valor_COFINSAliq": "NFe/infNFe/det/imposto/COFINS/COFINSAliq/vBC", + "valor_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/vBC" + }, + "aliquota_COFINSAliq": "NFe/infNFe/det/imposto/COFINS/COFINSAliq/pCOFINS", + "valor_COFINSAliq": "NFe/infNFe/det/imposto/COFINS/COFINSAliq/vCOFINS", + "cst_COFINSQtde": "NFe/infNFe/det/imposto/COFINS/COFINSQtde/CST", + "quantidadeVendida_COFINSQtde": "NFe/infNFe/det/imposto/COFINS/COFINSQtde/qBCProd", + "aliquotaReais_COFINSQtde": "NFe/infNFe/det/imposto/COFINS/COFINSQtde/vAliqProd", + "valor_COFINSQtde": "NFe/infNFe/det/imposto/COFINS/COFINSQtde/vCOFINS", + "cst_COFINSNT": "NFe/infNFe/det/imposto/COFINS/COFINSNT/CST", + "cst_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/CST", + "aliquota_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/pCOFINS", + "quantidadeVendida_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/qBCProd", + "aliquotaReais_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/vAliqProd", + "valor_COFINSOutr": "NFe/infNFe/det/imposto/COFINS/COFINSOutr/vCOFINS", + "substituicaoTributaria": { + "baseCalculo": "NFe/infNFe/det/imposto/COFINSST/vBC", + "aliquota": "NFe/infNFe/det/imposto/COFINSST/pCOFINS", + "quantidadeVendida": "NFe/infNFe/det/imposto/COFINSST/qBCProd", + "aliquotaReais": "NFe/infNFe/det/imposto/COFINSST/vAliqProd", + "valor": "NFe/infNFe/det/imposto/COFINSST/vCOFINS", + "compoeTotal": "NFe/infNFe/det/imposto/COFINSST/indSomaCOFINSST" + } + }, + "partilha": { + "ufDestino": { + "baseCalculoIcms": "NFe/infNFe/det/imposto/ICMSUFDest/vBCUFDest", + "baseCalculoFcp": "NFe/infNFe/det/imposto/ICMSUFDest/vBCFCPUFDest", + "percentualIcmsFcp": "NFe/infNFe/det/imposto/ICMSUFDest/pFCPUFDest", + "aliquotaInterna": "NFe/infNFe/det/imposto/ICMSUFDest/pICMSUFDest", + "aliquotaInterestadual": "NFe/infNFe/det/imposto/ICMSUFDest/pICMSInter", + "percentualProvisorioIcmsInterestadual": "NFe/infNFe/det/imposto/ICMSUFDest/pICMSInterPart", + "icmsRelativoFcp": "NFe/infNFe/det/imposto/ICMSUFDest/vFCPUFDest", + "icmsInterestadual": "NFe/infNFe/det/imposto/ICMSUFDest/vICMSUFDest" + }, + "ufRemetente": { + "icmsInterestadual": "NFe/infNFe/det/imposto/ICMSUFDest/vICMSUFRemet" + } + }, + "percentualMercadoriaDevolvida": "NFe/infNFe/det/impostoDevol/pDevol", + "valorIpiDevolvido": "NFe/infNFe/det/impostoDevol/IPI/vIPIDevol" + }, + "informacoesComplementares": "NFe/infNFe/det/infAdProd", + "numero": "NFe/infNFe/det/@nItem", + "nve": [{"numero": "NFe/infNFe/det/prod/NVE"}], + "importacao": [{ + "numero": "NFe/infNFe/det/prod/DI/nDI", + "dataEmissao": "NFe/infNFe/det/prod/DI/dDI", + "desembaraco": { + "local": "NFe/infNFe/det/prod/DI/xLocDesemb", + "estado": "NFe/infNFe/det/prod/DI/UFDesemb", + "data": "NFe/infNFe/det/prod/DI/dDesemb" + }, + "viaTransporte": "NFe/infNFe/det/prod/DI/tpViaTransp", + "valorAfrmm": "NFe/infNFe/det/prod/DI/vAFRMM", + "formaImportacao": "NFe/infNFe/det/prod/DI/tpIntermedio", + "adquirente": { + "cnpj": "NFe/infNFe/det/prod/DI/CNPJ", + "cpf": "NFe/infNFe/det/prod/DI/CPF", + "estado": "NFe/infNFe/det/prod/DI/UFTerceiro" + }, + "codigoExportador": "NFe/infNFe/det/prod/DI/cExportador", + "adicoes": [{ + "numero": "NFe/infNFe/det/prod/DI/adi/nAdicao", + "numeroSequencia": "NFe/infNFe/det/prod/DI/adi/nSeqAdic", + "codigoFabricante": "NFe/infNFe/det/prod/DI/adi/cFabricante", + "valorDesconto": "NFe/infNFe/det/prod/DI/adi/vDescDI", + "numeroDrawback": "NFe/infNFe/det/prod/DI/adi/nDraw" + }] + }], + "exportacao": [{ + "numeroDrawback": "NFe/infNFe/det/prod/detExport/nDraw", + "exportacaoIndireta": { + "numero": "NFe/infNFe/det/prod/detExport/exportInd/nRE", + "chave": "NFe/infNFe/det/prod/detExport/exportInd/chNFe", + "quantidade": "NFe/infNFe/det/prod/detExport/exportInd/qExport" + } + }], + "rastreavel": [{ + "lote": "NFe/infNFe/det/prod/rastro/nLote", + "quantidade": "NFe/infNFe/det/prod/rastro/qLote", + "dataFabricacao": "NFe/infNFe/det/prod/rastro/dFab", + "dataValidade": "NFe/infNFe/det/prod/rastro/dVal", + "codigoAgregacao": "NFe/infNFe/det/prod/rastro/cAgreg" + }], + "armamentos": [{ + "tipo": "NFe/infNFe/det/prod/arma/tpArma", + "numeroSerie": "NFe/infNFe/det/prod/arma/nSerie", + "numeroSerieCano": "NFe/infNFe/det/prod/arma/nCano", + "descricao": "NFe/infNFe/det/prod/arma/descr" + }], + "observacoes": { + "contribuinte": [{ + "campo": "NFe/infNFe/det/obsItem/obsCont/@xCampo", + "texto": "NFe/infNFe/det/obsItem/obsCont/xTexto" + }], + "fisco": [{ + "texto": "NFe/infNFe/det/obsItem/obsFisco/@xCampo", + "campo": "NFe/infNFe/det/obsItem/obsFisco/xTexto" + }] + }, + "valorTotal": "NFe/infNFe/det/vItem", + "dfeReferenciado": { + "chaveAcesso": "NFe/infNFe/det/DFeReferenciado/chaveAcesso", + "numero": "NFe/infNFe/det/DFeReferenciado/nItem" + } + }], + "pagamentos": [{ + "aVista": "NFe/infNFe/pag/detPag/indPag", + "meio": "NFe/infNFe/pag/detPag/tPag", + "descricaoMeio": "NFe/infNFe/pag/detPag/xPag", + "valor": "NFe/infNFe/pag/detPag/vPag", + "cartao": { + "tipoIntegracao": "NFe/infNFe/pag/detPag/card/tpIntegra", + "cnpjCredenciadora": "NFe/infNFe/pag/detPag/card/CNPJ", + "bandeiraOperadora": "NFe/infNFe/pag/detPag/card/tBand", + "numeroAutorizacao": "NFe/infNFe/pag/detPag/card/cAut" + } + }], + "pagamento": { + "data": "NFe/infNFe/pag/detPag/dPag", + "cnpjTransacional": "NFe/infNFe/pag/detPag/CNPJPag", + "ufTransacional": "NFe/infNFe/pag/detPag/UFPag", + "cartao": { + "cnpjBeneficiario": "NFe/infNFe/pag/detPag/card/CNPJReceb", + "idTerminal": "NFe/infNFe/pag/detPag/card/idTermPag" + } + }, + "intermediadorTransacao": { + "cpfCnpj": "NFe/infNFe/infIntermed/CNPJ", + "identificadorCadastro": "NFe/infNFe/infIntermed/idCadIntTran" + }, + "observacoes": { + "contribuinte": [{ + "campo": "NFe/infNFe/infAdic/obsCont/@xCampo", + "texto": "NFe/infNFe/infAdic/obsCont/xTexto" + }], + "fisco": [{ + "texto": "NFe/infNFe/infAdic/obsFisco/xTexto", + "campo": "NFe/infNFe/infAdic/obsFisco/@xCampo" + }] + }, + "processoReferenciado": [{ + "identificador": "NFe/infNFe/infAdic/procRef/nProc", + "origem": "NFe/infNFe/infAdic/procRef/indProc", + "tipo": "NFe/infNFe/infAdic/procRef/tpAto" + }], + "responsavelTecnico": { + "cpfCnpj": "NFe/infNFe/infRespTec/CNPJ", + "nome": "NFe/infNFe/infRespTec/xContato", + "email": "NFe/infNFe/infRespTec/email", + "telefone": "NFe/infNFe/infRespTec/fone", + "idCSRT": "NFe/infNFe/infRespTec/idCSRT", + "hashCSRT": "NFe/infNFe/infRespTec/hashCSRT" + }, + "agropecuario": { + "defensivo": [{ + "numeroReceituario": "NFe/infNFe/agropecuario/defensivo/nReceituario", + "cpfResponsavelTecnico": "NFe/infNFe/agropecuario/defensivo/CPFRespTec" + }], + "guiaTransito": { + "tipoGuia": "NFe/infNFe/agropecuario/guiaTransito/tpGuia", + "ufGuia": "NFe/infNFe/agropecuario/guiaTransito/UFGuia", + "serieGuia": "NFe/infNFe/agropecuario/guiaTransito/serieGuia", + "numeroGuia": "NFe/infNFe/agropecuario/guiaTransito/nGuia" + } + } +} \ No newline at end of file diff --git a/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd b/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd new file mode 100644 index 00000000..44e4ec49 --- /dev/null +++ b/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd @@ -0,0 +1,1097 @@ + + + + + + Tipo string genérico + + + + + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Código de Classificação Tributária do IBS e da CBS + + + + + + + + + Código de Classificação do Crédito Presumido do IBS e da CBS, conforme tabela cCredPres + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 11 de corpo e 4 decimais + + + + + + + + + Tipo Decimal com 11 inteiros, podendo ter 4 decimais (utilizado em tags opcionais) + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais + + + + + + + + + Tipo Decimal com até 3 dígitos inteiros, podendo ter de 2 até 4 decimais + + + + + + + + + Tipo da Operação com Ente Governamental + + + + + + + + + + Tipo de Ente Governamental + + + + + + + + + + + + Tipo de classificação do Crédito Presumido IBS ZFM + + + + + + + + + + + + Grupo de informações da Tributação da NFCom + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Grupo de informações da Tributação da NF3e + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Grupo de informações da Tributação do CTe + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Grupo de informações da Tributação do BPe + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Grupo de informações da Tributação da NFCe + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + + + + Grupo de informações da Tributação da NFe + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + Informar essa opção da Choice para Monofasia + + + + + Informar essa opção da Choice para o CST 800 + + + + + + Classificação de acordo com o art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido na ZFM + + + + + + + Grupo de informações do Imposto Seletivo + + + + + Código Situação Tributária do Imposto Seletivo + + + + + + + Valor do BC + + + + + Alíquota do Imposto Seletivo (percentual) + + + + + Alíquota do Imposto Seletivo (por valor) + + + + + + Unidade de medida apropriada especificada em Lei Ordinaria para fins de apuração do Imposto Seletivo + + + + + + + + + + + Quantidade com abse no campo uTrib informado + + + + + + Valor do Imposto Seletivo calculado + + + + + + + + Grupo de informações de totais do Imposto Seletivo + + + + + Valor Total do Imposto Seletivo + + + + + + + Grupo de informações de totais da CBS/IBS + + + + + Total Base de Calculo + + + + + Totalização do IBS + + + + + + Totalização do IBS de competência da UF + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total do IBS Estadual + + + + + + + + Totalização do IBS de competência Municipal + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total do IBS Municipal + + + + + + + + Valor total do IBS + + + + + Total do Crédito Presumido + + + + + Total do Crédito Presumido Condição Suspensiva + + + + + + + + Totalização da CBS + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total da CBS + + + + + Total do Crédito Presumido + + + + + Total do Crédito Presumido Condição Suspensiva + + + + + + + + + + Grupo de informações de totais da CBS/IBS com monofasia + + + + + Total Base de Calculo + + + + + Totalização do IBS + + + + + + Totalização do IBS de competência da UF + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total do IBS Estadual + + + + + + + + Totalização do IBS de competência Municipal + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total do IBS Municipal + + + + + + + + Valor total do IBS + + + + + Total do Crédito Presumido + + + + + Total do Crédito Presumido Condição Suspensiva + + + + + + + + Totalização da CBS + + + + + + Total do Diferimento + + + + + Total de devoluções de tributos + + + + + Valor total da CBS + + + + + Total do Crédito Presumido + + + + + Total do Crédito Presumido Condição Suspensiva + + + + + + + + Totais da Monofasia + Só deverá ser utilizado para DFe modelos 55 e 65 + + + + + + Valor total do IBS monofásico + + + + + Valor total da CBS monofásica + + + + + Valor total do IBS monofásico sujeito a retenção + + + + + Valor total da CBS monofásica sujeita a retenção + + + + + Valor do IBS monofásico retido anteriormente + + + + + Valor da CBS monofásica retida anteriormente + + + + + + + + + + Tipo Monofasia + + + + Monofasia + + + + Grupo de informações da Tributação Monofásica padrão + + + + + + Quantidade tributada na monofasia + + + + + Alíquota ad rem do IBS + + + + + Alíquota ad rem da CBS + + + + + Valor do IBS monofásico + + + + + Valor da CBS monofásica + + + + + + + + Grupo de informações da Tributação Monofásica sujeita a retenção + + + + + + Quantidade tributada sujeita a retenção. + + + + + Alíquota ad rem do IBS sujeito a retenção + + + + + Valor do IBS monofásico sujeito a retenção + + + + + Alíquota ad rem da CBS sujeita a retenção + + + + + Valor da CBS monofásica sujeita a retenção + + + + + + + + Grupo de informações da Tributação Monofásica retida anteriormente + + + + + + Quantidade tributada retida anteriormente + + + + + Alíquota ad rem do IBS retido anteriormente + + + + + Valor do IBS retido anteriormente + + + + + Alíquota ad rem da CBS retida anteriormente + + + + + Valor da CBS retida anteriormente + + + + + + + + Grupo de informações do diferimento da Tributação Monofásica + + + + + + Percentual do diferimento do imposto monofásico + + + + + Valor do IBS monofásico diferido + + + + + Percentual do diferimento do imposto monofásico + + + + + Valor da CBS monofásica diferida + + + + + + + + Total de IBS monofásico do item + + + + + Total da CBS monofásica do item + + + + + + + Tipo CBS IBS Completo + + + + IBS / CBS + + + + Valor do BC + + + + + + Grupo de informações do IBS na UF + + + + + + Aliquota do IBS de competência das UF + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Valor do IBS de competência das UF + + + + + + + + Grupo de Informações do IBS no Município + + + + + + Aliquota do IBS Municipal + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Valor do IBS Municipal + + + + + + + + Valor do IBS + + + + + + Grupo de Tributação da CBS + + + + + + Aliquota da CBS + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Valor da CBS + + + + + + + + Grupo de informações da Tributação Regular. Informar como seria a tributação caso não cumprida a condição resolutória/suspensiva. Exemplo 1: Art. 442, §4. Operações com ZFM e ALC. Exemplo 2: Operações com suspensão do tributo. + + + + + Grupo de Informações do Crédito Presumido referente ao IBS, quando aproveitado pelo emitente do documento. + + + + + Grupo de Informações do Crédito Presumido referente a CBS, quando aproveitado pelo emitente do documento. + + + + + Grupo de informações da composição do valor do IBS e da CBS em compras governamental + + + + + + + Tipo Redução Base de Cálculo + + + + + Percentual de redução de aliquota do cClassTrib + + + + + Aliquota Efetiva que será aplicada a Base de Calculo + + + + + + + Tipo Crédito Presumido + + + + + Código de Classificação do Crédito Presumido do IBS e da CBS + + + + + Percentual do Crédito Presumido + + + + + + Valor do Crédito Presumido + + + + + Valor do Crédito Presumido Condição Suspensiva, preencher apenas para cCredPres que possui indicação de Condição Suspensiva + + + + + + + + Tipo Diferimento + + + + + Percentual do diferimento + + + + + Valor do diferimento + + + + + + + Tipo Devolução Tributo + + + + + Valor do tributo devolvido. No fornecimento de energia elétrica, água, esgoto e +gás natural e em outras hipóteses definidas no regulamento + + + + + + + Tipo Tributação Regular + + + + + Código da Situação Tributária do IBS e CBS + Informar qual seria o CST caso não cumprida a condição resolutória/suspensiva + + + + + Informar qual seria o cClassTrib caso não cumprida a condição resolutória/suspensiva + + + + + Alíquota do IBS da UF + Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva + + + + + Valor do IBS da UF + Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva + + + + + Alíquota do IBS do Município + Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva + + + + + Valor do IBS do Município + Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva + + + + + Alíquota da CBS + Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva + + + + + Valor da CBS + Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva + + + + + + + Tipo Tributação Compra Governamental + + + + + + Valor que seria devido a UF, sem aplicação do Art. 473. da LC 214/2025 + + + + + + Valor que seria devido ao município, sem aplicação do Art. 473. da LC 214/2025 + + + + + + Valor que seria devido a CBS, sem aplicação do Art. 473. da LC 214/2025 + + + + + + + Tipo Compras Governamentais + Cada DFe que utilizar deverá utilizar esses tipo no grupo ide + + + + + Para administração pública direta e suas autarquias e fundações: +1=União +2=Estados +3=Distrito Federal +4=Municípios + + + + + Percentual de redução de aliquota em compra goverrnamental + + + + + + + Tipo Compras Governamentais + Cada DFe que utilizar deverá utilizar esses tipo no grupo ide + + + + + Para administração pública direta e suas autarquias e fundações: +1=União +2=Estados +3=Distrito Federal +4=Municípios + + + + + Percentual de redução de aliquota em compra goverrnamental + + + + + Tipo da operação com ente governamental: +1 - Fornecimento +2 - Recebimento do Pagamento + + + + + + + Tipo Transferência de Crédito + + + + + Valor do IBS a ser transferido + + + + + Valor da CBS a ser transferida + + + + + + + Tipo Informações do crédito presumido de IBS para fornecimentos a partir da ZFM + + + + + Classificação de acordo com o art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido na ZFM + 0 - Sem crédito presumido; +1 - Bens de consumo final (55%); +2 - Bens de capital (75%); +3 - Bens intermediários (90,25%); +4 - Bens de informática e outros definidos em legislação (100%). +OBS: Percentuais definidos no art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido + + + + + + Valor do crédito presumido calculado sobre o saldo devedor apurado + + + + + diff --git a/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd b/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd index 8f0ccd22..77067626 100644 --- a/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd +++ b/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd @@ -1,5 +1,5 @@ - + @@ -17,10 +17,11 @@ - + + Tipo Nota Fiscal Eletrônica @@ -123,6 +124,12 @@ SCAN 900-999 Código do Município de Ocorrência do Fato Gerador (utilizar a tabela do IBGE) + + + Informar o município de ocorrência do fato gerador do fato gerador do IBS / CBS. +Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do estabelecimento) ”, e não tiver endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01). + + Formato de impressão do DANFE (0-sem DANFE;1-DANFe Retrato; 2-DANFe Paisagem;3-DANFe Simplificado; @@ -190,7 +197,25 @@ SCAN 900-999 1 - NFe normal 2 - NFe complementar 3 - NFe de ajuste -4 - Devolução/Retorno +4 - Devolução/Retorno +5 - Nota de crédito +6 - Nota de débito + + + + + Tipo de Nota de Débito: +01=Transferência de créditos para Cooperativas; +02=Anulação de Crédito por Saídas Imunes/Isentas; +03=Débitos de notas fiscais não processadas na apuração; +04=Multa e juros; +05=Transferência de crédito de sucessão. + + + + + + Tipo de Nota de Crédito @@ -466,6 +491,25 @@ Preencher com "2B", quando se tratar de Cupom Fiscal emitido por máqu + + + Grupo de Compras Governamentais + + + + + Informado para abater as parcelas de antecipação de pagamento, conforme Art. 10. § 4º + + + + + + Chave de acesso da NF-e de antecipação de pagamento + + + + + @@ -1037,7 +1081,7 @@ Formato ”CFOP9999”. - Valor unitário de tributação - - alterado para aceitar 0 a 10 casas decimais e 11 inteiros + Valor unitário de tributação - alterado para aceitar 0 a 10 casas decimais e 11 inteiros @@ -1074,10 +1118,20 @@ Formato ”CFOP9999”. + + + Indicador de fornecimento de bem móvel usado: 1-Bem Móvel Usado + + + + + + + + - Delcaração de Importação -(NT 2011/004) + Declaração de Importação (NT 2011/004) @@ -2041,7 +2095,7 @@ ambiente. Valor estimado total de impostos federais, estaduais e municipais - + @@ -5019,6 +5073,16 @@ Substituição Tributaria; + + + Grupo de informações do Imposto Seletivo + + + + + Grupo de informações dos tributos IBS, CBS e Imposto Seletivo + + @@ -5117,6 +5181,36 @@ Substituição Tributaria; + + + Valor total do Item, correspondente à sua participação no total da nota. A soma dos itens deverá corresponder ao total da nota. + + + + + Referenciamento de item de outros DFe + + + + + + Chave de Acesso do DFe referenciado + + + + + Número do item do documento referenciado. Corresponde ao atributo nItem do elemento det do documento original. + + + + + + + + + + + @@ -5415,6 +5509,21 @@ Substituição Tributaria; + + + Valores totais da NF com Imposto Seletivo + + + + + Valores totais da NF com IBS / CBS + + + + + Valor Total da NF considerando os impostos por fora IBS, CBS e IS + + @@ -6292,7 +6401,7 @@ tipo de ato concessório: - + Defensivo Agrícola / Agrotóxico @@ -6303,8 +6412,8 @@ tipo de ato concessório: Número do Receituário ou Receita do Defensivo / Agrotóxico - - + + @@ -6340,7 +6449,7 @@ tipo de ato concessório: - + Série da Guia @@ -6403,9 +6512,18 @@ tipo de ato concessório: - - - + + + + + + + + + + + + @@ -7221,6 +7339,34 @@ alterado para tamanho variavel 1-4. (NT2011/004) + + + + + + + Tipo de Nota de Débito: 01=Transferência de créditos para Cooperativas; 02=Anulação de Crédito por Saídas Imunes/Isentas; 03=Débitos de notas fiscais não processadas na apuração; 04=Multa e juros; 05=Transferência de crédito de sucessão); 06=Pagamento antecipado; 07=Perda em estoque + + + + + + + + + + + + + + + Tipo de Nota de Crédito: 01=Multa e juros; 02=Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM (art. 450, § 1º, LC 214/25); 03=Retorno + + + + + + @@ -7409,4 +7555,4 @@ alterado para tamanho variavel 1-4. (NT2011/004) - + \ No newline at end of file diff --git a/pynfe/utils/flags.py b/pynfe/utils/flags.py index fe9b3449..589eab61 100644 --- a/pynfe/utils/flags.py +++ b/pynfe/utils/flags.py @@ -34,6 +34,12 @@ XSD_PD_CANCELAR_NFE = "procCancNFe_v1.07.xsd" XSD_PD_INUTILIZAR_NFE = "procInutNFe_v1.07.xsd" +XSD_NFE_LEIAUTE = "leiauteNFe_v4.00.xsd" +XSD_NFE_TIPOS = "DFeTiposBasicos_v1.00.xsd" + +SCHEMA_FOLDER_NFE = "pynfe/data/SCHEMAs/NF-e" +SCHEMA_NFE = "schemaNFe_v1.00.json" + XSD_FOLDER_MDFE = "pynfe/data/XSDs/MDF-e" XSD_MDFE = "mdfe_v3.00.xsd" XSD_MDFE_PROCESSADA = "procMDFe_v3.00.xsd" @@ -43,15 +49,18 @@ ("00", "ICMS 00 - Tributada integralmente"), ("02", "ICMS 02 - Tributação monofásica própria sobre combustíveis"), ("10", "ICMS 10 - Tributada com cobranca do ICMS por substituicao tributaria"), - ("15", "ICMS 15 - Tributação monofásica própria e com responsabilidade pela retenção sobre combustíveis"), - ("20", "ICMS 20 - Com reducao da base de calculo"), ( - "30", + "15", ( - "ICMS 30 - Isenta ou nao tributada e com cobranca do ICMS por substituicao" - " tributaria" + "ICMS 15 - Tributação monofásica própria e com responsabilidade pela retenção sobre" + " combustíveis" ), ), + ("20", "ICMS 20 - Com reducao da base de calculo"), + ( + "30", + "ICMS 30 - Isenta ou nao tributada e com cobranca do ICMS por substituicao tributaria", + ), ("40", "ICMS 40 - Isenta"), ("41", "ICMS 41 - Nao tributada"), ("50", "ICMS 50 - Suspensao"), @@ -61,10 +70,7 @@ ("61", "ICMS 61 - Tributação monofásica sobre combustíveis cobrada anteriormente"), ( "70", - ( - "ICMS 70 - Com reducao da base de calculo e cobranca do ICMS por" - " substituicao tributaria" - ), + "ICMS 70 - Com reducao da base de calculo e cobranca do ICMS por substituicao tributaria", ), ("90", "ICMS 90 - Outras"), ("101", "ICMS 101 - Tributação ICMS pelo Simples Nacional, CSOSN=101"), @@ -106,10 +112,7 @@ ), ( 5, - ( - "Nacional, mercadoria ou bem com Conteúdo de Importação inferior ou igual a" - " 40%. " - ), + "Nacional, mercadoria ou bem com Conteúdo de Importação inferior ou igual a 40%. ", ), ( 6, From 5581c25425a6aec98616ff9f83ff6a9764a55808 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 23 Aug 2025 22:37:07 -0300 Subject: [PATCH 037/175] fix: update version to 0.5.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d8524fea..12070ff4 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.2", + version="0.5.3", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 91c2b8c99eadcfc0d9bd0085886287cdce305a01 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 23 Aug 2025 23:33:14 -0300 Subject: [PATCH 038/175] fix: update version to 0.5.4 and add additional data file types in package_data --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 12070ff4..543f6497 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.3", + version="0.5.4", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", @@ -27,7 +27,7 @@ ], packages=setuptools.find_packages(exclude=["tests"]), package_data={ - "pynfe": ["data/**/*.txt"], + "pynfe": ["data/**/*.txt", "data/**/*.json", "data/**/*.xsd", "data/**/*.csv"], }, install_requires=[ "pyopenssl>=23.0.0", From 2b5794227cae70563a642c5f0c4f6932ea6b69a0 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 25 Aug 2025 15:17:24 -0300 Subject: [PATCH 039/175] feat: add valorIbs field to IBSCBS schema for improved tax calculations --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index 44b0c686..4e6b7b95 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -774,6 +774,7 @@ }, "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSMun/vIBSMun" }, + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/vIBS", "cbs": { "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBS/pCBS", "diferimento": { @@ -825,6 +826,7 @@ "aliquotaAdRemCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBS", "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMono", "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMono", + "quantidadeRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMonoReten", "aliquotaAdRemIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBSReten", "valorIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoReten", From 0f7d6249e3841fe691605f69bd57a63ccaf7fe5d Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 25 Aug 2025 16:35:14 -0300 Subject: [PATCH 040/175] fix: update version to 0.5.5 in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 543f6497..922ca168 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.4", + version="0.5.5", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From c1d0d8ec26c095ef848af32f4a39a9cc4aa4e9b6 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 26 Aug 2025 08:02:49 -0300 Subject: [PATCH 041/175] fix: ajusts NT 1.20 --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 44 ++++++++++---------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index 4e6b7b95..a919e674 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -821,28 +821,30 @@ "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/vTribCBS" }, "monofasico": { - "quantidade": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMono", - "aliquotaAdRemIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBS", - "aliquotaAdRemCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBS", - "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMono", - "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMono", + "quantidade": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoPadrao/qBCMono", + "aliquotaAdRemIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoPadrao/adRemIBS", + "aliquotaAdRemCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoPadrao/adRemCBS", + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoPadrao/vIBSMono", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoPadrao/vCBSMono", - "quantidadeRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMonoReten", - "aliquotaAdRemIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBSReten", - "valorIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoReten", - "aliquotaAdRemCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBSReten", - "valorCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoReten", - "quantidadeRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/qBCMonoRet", - "aliquotaAdRemIbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemIBSRet", - "valorIbsRetido": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoRet", - "aliquotaAdRemCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/adRemCBSRet", - "valorCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoRet", - "aliquotaIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/pDifIBS", - "valorIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vIBSMonoDif", - "aliquotaCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/pDifCBS", - "valorCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vCBSMonoDif", - "valorTotalIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vTotIBSMonoItem", - "valorTotalCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/vTotCBSMonoItem" + "quantidadeRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoReten/qBCMonoReten", + "aliquotaAdRemIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoReten/adRemIBSReten", + "valorIbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoReten/vIBSMonoReten", + "aliquotaAdRemCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoReten/adRemCBSReten", + "valorCbsRetencao": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoReten/vCBSMonoReten", + + "quantidadeRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoRet/qBCMonoRet", + "aliquotaAdRemIbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoRet/adRemIBSRet", + "valorIbsRetido": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoRet/vIBSMonoRet", + "aliquotaAdRemCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoRet/adRemCBSRet", + "valorCbsRetida": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoRet/vCBSMonoRet", + + "aliquotaIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/pDifIBS", + "valorIbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/vIBSMonoDif", + "aliquotaCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/pDifCBS", + "valorCbsDiferimento": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/vCBSMonoDif", + "valorTotalIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/vTotIBSMonoItem", + "valorTotalCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/vTotCBSMonoItem" }, "transferenciaCredito": { "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gTransfCred/vIBS", From e18235b3fab1ad8199e4da89067446ffcbdf2def Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Mon, 1 Sep 2025 19:03:57 -0300 Subject: [PATCH 042/175] =?UTF-8?q?feat:=20add=20S=C3=A3o=20Paulo=20suppor?= =?UTF-8?q?t=20for=20NFSe=20communication=20and=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 38 ++++++++++++++++++++++++++++++ pynfe/utils/webservices.py | 6 +++++ 2 files changed, 44 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 713d4bc3..2ce17c81 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -628,6 +628,8 @@ def __init__(self, certificado, certificado_senha, autorizador, homologacao=Fals elif self.autorizador == "BETHA": self._namespace = NAMESPACE_BETHA self._versao = "2.02" + elif self.autorizador == "SAO_PAULO": + self._namespace = "http://www.prefeitura.sp.gov.br/nfe" else: raise Exception("Autorizador não encontrado!") @@ -770,6 +772,8 @@ def _get_url(self): """Retorna a url para comunicação com o webservice""" if self._ambiente == 1: ambiente = "HTTPS" + elif self._ambiente != 1 and self.autorizador == "SAO_PAULO": + raise Exception("São Paulo só opera em produção.") else: ambiente = "HOMOLOGACAO" if self.autorizador in NFSE: @@ -842,6 +846,40 @@ def _post_https(self, url, xml, metodo): except Exception as e: raise e + def enviar_sp(self, xml, operation): + url = self._get_url() + if self.autorizador == "SAO_PAULO": + return self._post_sp_https(url, xml, operation) + else: + raise Exception(f"Enviar RPS não implementado para {self.autorizador}") + + def _post_sp_https(self, url, xml, metodo): + """Comunicação wsdl (https) utilizando certificado do usuário""" + # comunicacao wsdl + try: + from pynfe.utils.https_nfse import HttpAuthenticated + from suds.client import Client + + certificadoA1 = CertificadoA1(self.certificado) + chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) + + cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) + + # gerar nfse + if metodo == "enviar_rps": + return cliente.service.EnviarRPS(VersaoSchema=1, MensagemXML=xml) + if metodo == "consultar_rps": + return cliente.service.ConsultaNFe(VersaoSchema=1, MensagemXML=xml) + elif metodo == "cancelar": + return cliente.service.CancelamentoNFe(VersaoSchema=1, MensagemXML=xml) + # TODO outros metodos + else: + raise Exception(f"Método {metodo} não implementado no autorizador São Paulo.") + except Exception as e: + raise e + finally: + certificadoA1.excluir() + class ComunicacaoMDFe(Comunicacao): MDFE_SITUACAO_JA_ENVIADO = ("100", "101", "132") diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 17e96716..0dac820f 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -500,6 +500,12 @@ # Nfs-e NFSE = { # + "SAO_PAULO": { + "ENVIAR_RPS": "EnviarRps", + "CONSULTA_RPS": "ConsultaNFe", + "CANCELAR_NFSE": "CancelamentoNFe", + "HTTPS": "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl" + }, "BETHA": { "AUTORIZACAO": "GerarNfse", "CANCELAR": "CancelarNfse", From 0853ee68f43adab227a276d9494b3d6e405e1c3b Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Mon, 8 Sep 2025 22:00:58 -0300 Subject: [PATCH 043/175] wip --- pynfe/processamento/comunicacao.py | 43 ++++++++++++++++++++++++++++++ pynfe/utils/webservices.py | 7 +++++ 2 files changed, 50 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 2ce17c81..4a27ddd4 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -630,6 +630,10 @@ def __init__(self, certificado, certificado_senha, autorizador, homologacao=Fals self._versao = "2.02" elif self.autorizador == "SAO_PAULO": self._namespace = "http://www.prefeitura.sp.gov.br/nfe" + self._versao = "2" + elif self.autorizador == "BARUERI": + self._namespace = "http://www.barueri.sp.gov.br/nfeservice" + self._versao = "1" else: raise Exception("Autorizador não encontrado!") @@ -846,6 +850,45 @@ def _post_https(self, url, xml, metodo): except Exception as e: raise e + def enviar_barueri(self, xml, operation): + url = self._get_url() + if self.autorizador == "BARUERI": + return self._post_barueri_https(url, xml, operation) + else: + raise Exception(f"Enviar RPS não implementado para {self.autorizador}") + + def _post_barueri_https(self, url, xml, metodo): + """Comunicação wsdl (https) utilizando certificado do usuário""" + # comunicacao wsdl + try: + from pynfe.utils.https_nfse import HttpAuthenticated + from suds.client import Client + + certificadoA1 = CertificadoA1(self.certificado) + chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) + + cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) + + # gerar nfse + + if metodo == "enviar_rps": + return cliente.service.NFeLoteEnviarArquivo(VersaoSchema=1, MensagemXML=xml) + elif metodo == "consultar_rps": + return cliente.service.ConsultaNFe(VersaoSchema=1, MensagemXML=xml) + elif metodo == "listar_rps": + return cliente.service.NFeLoteListarArquivos(VersaoSchema=1, MensagemXML=xml) + elif metodo == "baixar_nfse": + return cliente.service.NFeLoteBaixarArquivo(VersaoSchema=1, MensagemXML=xml) + elif metodo == "cancelar": + return cliente.service.CancelamentoNFe(VersaoSchema=1, MensagemXML=xml) + # TODO outros metodos + else: + raise Exception(f"Método {metodo} não implementado no autorizador Barueri.") + except Exception as e: + raise e + finally: + certificadoA1.excluir() + def enviar_sp(self, xml, operation): url = self._get_url() if self.autorizador == "SAO_PAULO": diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 0dac820f..4cccb546 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -506,6 +506,13 @@ "CANCELAR_NFSE": "CancelamentoNFe", "HTTPS": "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl" }, + "BARUERI": { + "ENVIAR_RPS": "EnviarRPS", + "CONSULTA_RPS": "ConsultaNFe", + "CANCELAR_NFSE": "CancelamentoNFe", + "HTTPS": "https://www.barueri.sp.gov.br/nfeservice/wsrps.asmx?WSDL", + "HOMOLOGACAO": "https://testeeiss.barueri.sp.gov.br/nfeservice/wsrps.asmx?WSDL" + }, "BETHA": { "AUTORIZACAO": "GerarNfse", "CANCELAR": "CancelarNfse", From 5b299579ef02134dd1aac0ee716b990a5d8da4e4 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Sat, 20 Sep 2025 14:30:45 -0300 Subject: [PATCH 044/175] fix: fix certificadoA1 initialization --- pynfe/processamento/comunicacao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 4a27ddd4..f6c2f04d 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -860,11 +860,12 @@ def enviar_barueri(self, xml, operation): def _post_barueri_https(self, url, xml, metodo): """Comunicação wsdl (https) utilizando certificado do usuário""" # comunicacao wsdl + + certificadoA1 = CertificadoA1(self.certificado) try: from pynfe.utils.https_nfse import HttpAuthenticated from suds.client import Client - certificadoA1 = CertificadoA1(self.certificado) chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) From f26517a7206e0987bf49caf0614770b743c8f8d2 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Sat, 20 Sep 2025 14:35:28 -0300 Subject: [PATCH 045/175] fix: remove redundant certificadoA1 initialization in ComunicacaoNfse class fix: add suds-jurko to main dependencies in setup.py --- pynfe/processamento/comunicacao.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index f6c2f04d..0167d0df 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -900,11 +900,11 @@ def enviar_sp(self, xml, operation): def _post_sp_https(self, url, xml, metodo): """Comunicação wsdl (https) utilizando certificado do usuário""" # comunicacao wsdl + certificadoA1 = CertificadoA1(self.certificado) try: from pynfe.utils.https_nfse import HttpAuthenticated from suds.client import Client - certificadoA1 = CertificadoA1(self.certificado) chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) diff --git a/setup.py b/setup.py index 922ca168..40113f38 100644 --- a/setup.py +++ b/setup.py @@ -34,10 +34,10 @@ "requests", "lxml", "signxml", + "suds-jurko", ], extras_require={ "nfse": [ - "suds-jurko", "pyxb==1.2.4", ], }, From a608a34226f1c09359376584a5621e1a2b609c61 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Sat, 20 Sep 2025 14:36:33 -0300 Subject: [PATCH 046/175] fix: update version to 0.6.0 in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 40113f38..501d115b 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.5", + version="0.6.0", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 45034b734656ff897d85f33f6641f7fcfb66c4a3 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Sat, 20 Sep 2025 14:42:12 -0300 Subject: [PATCH 047/175] fix: update suds-jurko to suds-py3 in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 501d115b..7f4ed251 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ "requests", "lxml", "signxml", - "suds-jurko", + "suds-py3", ], extras_require={ "nfse": [ From 0136c79932d09dc08ed1c74a0b7bb0d833aec025 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Sat, 20 Sep 2025 17:11:54 -0300 Subject: [PATCH 048/175] fix: update version to 0.6.1 in setup.py and correct method call in ComunicacaoNfse class --- pynfe/processamento/comunicacao.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 0167d0df..844b0048 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -875,7 +875,7 @@ def _post_barueri_https(self, url, xml, metodo): if metodo == "enviar_rps": return cliente.service.NFeLoteEnviarArquivo(VersaoSchema=1, MensagemXML=xml) elif metodo == "consultar_rps": - return cliente.service.ConsultaNFe(VersaoSchema=1, MensagemXML=xml) + return cliente.service.NFeLoteStatusArquivo(VersaoSchema=1, MensagemXML=xml) elif metodo == "listar_rps": return cliente.service.NFeLoteListarArquivos(VersaoSchema=1, MensagemXML=xml) elif metodo == "baixar_nfse": diff --git a/setup.py b/setup.py index 7f4ed251..17c35434 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.6.0", + version="0.6.1", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 0be3d4e873ea326094a6fa6b4d2acb7257d95654 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Mon, 22 Sep 2025 18:59:48 -0300 Subject: [PATCH 049/175] fix: update version to 0.7.0 in setup.py and implement new request-based communication method for Barueri in ComunicacaoNfse class --- pynfe/processamento/comunicacao.py | 50 ++++++++++++++++++++++++++++-- setup.py | 2 +- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 844b0048..9e06f09e 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -853,12 +853,58 @@ def _post_https(self, url, xml, metodo): def enviar_barueri(self, xml, operation): url = self._get_url() if self.autorizador == "BARUERI": - return self._post_barueri_https(url, xml, operation) + return self._post_barueri_requests(url, xml, operation) else: raise Exception(f"Enviar RPS não implementado para {self.autorizador}") + + def _post_barueri_requests(self, url, xml, operation): + """ + Comunicação SOAP usando requests diretamente (como NFe) + Recebe o envelope SOAP completo já montado + """ + import requests + + certificado_a1 = CertificadoA1(self.certificado) + try: + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) + chave_cert = (cert, chave) + + if operation == "enviar_rps": + soap_action = "http://www.barueri.sp.gov.br/nfe/NFeLoteEnviarArquivo" + elif operation == "consultar_rps": + soap_action = "http://www.barueri.sp.gov.br/nfe/NFeLoteStatusArquivo" + elif operation == "listar_rps": + soap_action = "http://www.barueri.sp.gov.br/nfe/NFeLoteListarArquivos" + elif operation == "baixar_nfse": + soap_action = "http://www.barueri.sp.gov.br/nfe/NFeLoteBaixarArquivo" + else: + raise Exception(f"Operação {operation} não implementada para Barueri.") + + headers = { + 'Content-Type': 'text/xml; charset=utf-8', + 'SOAPAction': f'"{soap_action}"' + } + + return requests.post( + url, + data=xml.encode('utf-8'), + headers=headers, + cert=chave_cert, + verify=False, + timeout=30 + ) + + except requests.exceptions.RequestException as e: + raise e + finally: + certificado_a1.excluir() def _post_barueri_https(self, url, xml, metodo): - """Comunicação wsdl (https) utilizando certificado do usuário""" + """ + LEGACY: Comunicação wsdl (https) utilizando certificado do usuário + Este método usa SUDS e não é mais usado pelo Barueri. + Usar _post_barueri_requests() para Barueri. + """ # comunicacao wsdl certificadoA1 = CertificadoA1(self.certificado) diff --git a/setup.py b/setup.py index 17c35434..351c7a2b 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.6.1", + version="0.7.0", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 11bf5999126dae301df5b80bc35340bab8047185 Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Mon, 22 Sep 2025 23:37:37 -0300 Subject: [PATCH 050/175] fix: update version to 0.7.1 in setup.py and add methods for RPS communication in ComunicacaoNfse class for Barueri --- pynfe/processamento/comunicacao.py | 23 ++++++++++++++++++++--- setup.py | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 9e06f09e..ba0d7314 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -852,11 +852,28 @@ def _post_https(self, url, xml, metodo): def enviar_barueri(self, xml, operation): url = self._get_url() - if self.autorizador == "BARUERI": - return self._post_barueri_requests(url, xml, operation) - else: + + if not self.autorizador == "BARUERI": raise Exception(f"Enviar RPS não implementado para {self.autorizador}") + return self._post_barueri_requests(url, xml, operation) + + def consultar_rps_barueri(self, xml, operation): + url = self._get_url() + + if not self.autorizador == "BARUERI": + raise Exception(f"Consultar RPS não implementado para {self.autorizador}") + + return self._post_barueri_requests(url, xml, operation) + + def baixar_rps_barueri(self, xml, operation): + url = self._get_url() + + if not self.autorizador == "BARUERI": + raise Exception(f"Baixar RPS não implementado para {self.autorizador}") + + return self._post_barueri_requests(url, xml, operation) + def _post_barueri_requests(self, url, xml, operation): """ Comunicação SOAP usando requests diretamente (como NFe) diff --git a/setup.py b/setup.py index 351c7a2b..0afd2750 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.7.0", + version="0.7.1", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 992e65a38d70702069df51584a3a82a72cfcfa3b Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 17 Oct 2025 09:21:35 -0300 Subject: [PATCH 051/175] feat: enhance NF-e schema with new fields for improved tax calculations --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 51 +- .../data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd | 468 +++++++++++++----- pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd | 39 +- 3 files changed, 396 insertions(+), 162 deletions(-) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index a919e674..b0920da6 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -10,6 +10,7 @@ "dataEmissao": "NFe/infNFe/ide/dhEmi", "dataSaidaEntradaTz": "NFe/infNFe/ide/dhSaiEnt", "saida": "NFe/infNFe/ide/tpNF", + "dataPrevistaEntrega": "NFe/infNFe/ide/dhPrevEntrega", "codigoIdentificacaoDestino": "NFe/infNFe/ide/idDest", "codigoMunicipioFatoGerador": "NFe/infNFe/ide/cMunFG", "codigoMunicipioFatoGeradorIBS": "NFe/infNFe/ide/cMunFGIBS", @@ -203,6 +204,8 @@ "valorCbsMonoRetenencao": "NFe/infNFe/total/IBSCBSTot/gMono/vCBSMonoReten", "valorIbsMonoRetido": "NFe/infNFe/total/IBSCBSTot/gMono/vIBSMonoRet", "valorCbsMonoRetido": "NFe/infNFe/total/IBSCBSTot/gMono/vCBSMonoRet", + "valorIbsEstornado": "NFe/infNFe/total/IBSCBSTot/gEstornoCred/vIBSEstCred", + "valorCbsEstornado": "NFe/infNFe/total/IBSCBSTot/gEstornoCred/vCBSEstCred", "valorIbsCbsIs": "NFe/infNFe/total/vNFTot" }, "transporte": { @@ -359,6 +362,7 @@ "valorDesconto": "NFe/infNFe/det/prod/vDesc", "valorOutros": "NFe/infNFe/det/prod/vOutro", "compoeTotal": "NFe/infNFe/det/prod/indTot", + "tipoCreditoPresumindoIbsZFM": "NFe/infNFe/det/prod/tpCredPresIBSZFM", "indicadorBemMovelUsado": "NFe/infNFe/det/prod/indBemMovelUsado", "numeroCompra": "NFe/infNFe/det/prod/xPed", "pedidoCompra": "NFe/infNFe/det/prod/nItemPed", @@ -743,6 +747,7 @@ "ibscbs": { "cst": "NFe/infNFe/det/imposto/IBSCBS/CST", "classificacao": "NFe/infNFe/det/imposto/IBSCBS/cClassTrib", + "indicadorDoacao": "NFe/infNFe/det/imposto/IBSCBS/indDoacao", "baseCalculo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/vBC", "uf": { "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSUF/pIBSUF", @@ -800,18 +805,6 @@ "aliquota": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/pAliqEfetRegCBS", "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribRegular/vTribRegCBS" }, - "ibsCreditoPresumido": { - "codigo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/cCredPres", - "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/pCredPres", - "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPres", - "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPresCondSus" - }, - "cbsCreditoPresumido": { - "codigo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/cCredPres", - "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/pCredPres", - "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPres", - "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPresCondSus" - }, "compraGovernamental": { "aliquotaUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/pAliqIBSUF", "valorUF": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTribCompraGov/vTribIBSUF", @@ -847,12 +840,36 @@ "valorTotalCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBSMono/gMonoDif/vTotCBSMonoItem" }, "transferenciaCredito": { - "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gTransfCred/vIBS", - "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gTransfCred/vCBS" + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTransfCred/vIBS", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gTransfCred/vCBS" + }, + "ajusteCompetencia": { + "competencia": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gAjusteCompet/competApur", + "valorIbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gAjusteCompet/vIBS", + "valorCbs": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gAjusteCompet/vCBS" + }, + "estornoCredito": { + "valorIbsEstornado": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gEstornoCred/vIBSEstCred", + "valorCbsEstornado": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gEstornoCred/vCBSEstCred" + }, + "creditoPresumidoOperacao": { + "codigo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCredPresOper/cCredPres", + "baseCalculo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCredPresOper/vBCCredPres" + }, + "ibsCreditoPresumido": { + "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/pCredPres", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPres", + "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gIBSCredPres/vCredPresCondSus" + }, + "cbsCreditoPresumido": { + "percentual": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/pCredPres", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPres", + "valorSuspensivo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCBSCredPres/vCredPresCondSus" }, - "creditoPresumidoZfm": { - "tipo": "NFe/infNFe/det/imposto/IBSCBS/gCredPresIBSZFM/tpCredPresIBSZFM", - "valor": "NFe/infNFe/det/imposto/IBSCBS/gCredPresIBSZFM/vCredPresIBSZFM" + "creditoPresumidoZfm": { + "competencia": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCredPresIBSZFM/competApur", + "tipo": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCredPresIBSZFM/tpCredPresIBSZFM", + "valor": "NFe/infNFe/det/imposto/IBSCBS/gIBSCBS/gCredPresIBSZFM/vCredPresIBSZFM" } }, "ipi": { diff --git a/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd b/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd index 44e4ec49..a5051dd0 100644 --- a/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd +++ b/pynfe/data/XSDs/NF-e/DFeTiposBasicos_v1.00.xsd @@ -1,5 +1,5 @@ - + @@ -37,7 +37,7 @@ - + Tipo Decimal com 15 dígitos, sendo 11 de corpo e 4 decimais @@ -46,7 +46,7 @@ - + Tipo Decimal com 11 inteiros, podendo ter 4 decimais (utilizado em tags opcionais) @@ -55,7 +55,7 @@ - + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais @@ -64,7 +64,7 @@ - + Tipo Decimal com até 3 dígitos inteiros, podendo ter de 2 até 4 decimais @@ -107,6 +107,14 @@ + + + Tipo Indicador de Doação + + + + + Grupo de informações da Tributação da NFCom @@ -118,7 +126,17 @@ + + + Indica se a operação é de doação + + + + + Informado conforme indicador no cClassTrib + + @@ -132,7 +150,37 @@ + + + Indica se a operação é de doação + + + + + + Informado conforme indicador no cClassTrib + + + + + + + Grupo de informações da Tributação da NFAg + + + + + Código Situação Tributária do IBS/CBS + + + + + + + Informado conforme indicador no cClassTrib + + @@ -146,7 +194,13 @@ + + + + Informado conforme indicador no cClassTrib + + @@ -160,7 +214,13 @@ + + + + Informado conforme indicador no cClassTrib + + @@ -174,6 +234,11 @@ + + + Indica se a operação é de doação + + @@ -191,11 +256,16 @@ + + + Indica se a operação é de doação + + - Informar essa opção da Choice para Monofasia + Informar essa opção da Choice para Monofasia (CST 620) @@ -203,10 +273,54 @@ Informar essa opção da Choice para o CST 800 + + + Informar essa opção da Choice para o CST 811 + + - + - Classificação de acordo com o art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido na ZFM + Informado conforme indicador no cClassTrib + + + + + + Crédito Presumido da Operação. Informado conforme indicador no cClassTrib. + + + + + Classificação de acordo com o art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido na ZFM. Informado conforme indicador no cClassTrib. + + + + + + + + Grupo de informações da Tributação da NFGas + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + + Informar essa opção da Choice para Monofasia + + + + + + Informado conforme indicador no cClassTrib @@ -223,17 +337,17 @@ - + Valor do BC - + Alíquota do Imposto Seletivo (percentual) - + Alíquota do Imposto Seletivo (por valor) @@ -250,13 +364,13 @@ - + Quantidade com abse no campo uTrib informado - + Valor do Imposto Seletivo calculado @@ -269,7 +383,7 @@ Grupo de informações de totais do Imposto Seletivo - + Valor Total do Imposto Seletivo @@ -281,7 +395,7 @@ Grupo de informações de totais da CBS/IBS - + Total Base de Calculo @@ -298,17 +412,17 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total do IBS Estadual @@ -322,17 +436,17 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total do IBS Municipal @@ -340,21 +454,11 @@ - + Valor total do IBS - - - Total do Crédito Presumido - - - - - Total do Crédito Presumido Condição Suspensiva - - @@ -364,29 +468,38 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total da CBS - + + + + + + Totalização do estorno de crédito + + + + - Total do Crédito Presumido + Valor total do IBS estornado - + - Total do Crédito Presumido Condição Suspensiva + Valor total da CBS estornada @@ -399,7 +512,7 @@ Grupo de informações de totais da CBS/IBS com monofasia - + Total Base de Calculo @@ -416,17 +529,17 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total do IBS Estadual @@ -440,17 +553,17 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total do IBS Municipal @@ -458,17 +571,17 @@ - + Valor total do IBS - + Total do Crédito Presumido - + Total do Crédito Presumido Condição Suspensiva @@ -482,27 +595,27 @@ - + Total do Diferimento - + Total de devoluções de tributos - + Valor total da CBS - + Total do Crédito Presumido - + Total do Crédito Presumido Condição Suspensiva @@ -517,32 +630,32 @@ - + Valor total do IBS monofásico - + Valor total da CBS monofásica - + Valor total do IBS monofásico sujeito a retenção - + Valor total da CBS monofásica sujeita a retenção - + Valor do IBS monofásico retido anteriormente - + Valor da CBS monofásica retida anteriormente @@ -550,6 +663,25 @@ + + + Totalização do estorno de crédito + + + + + + Valor total do IBS estornado + + + + + Valor total da CBS estornada + + + + + @@ -566,27 +698,27 @@ - + Quantidade tributada na monofasia - + Alíquota ad rem do IBS - + Alíquota ad rem da CBS - + Valor do IBS monofásico - + Valor da CBS monofásica @@ -600,27 +732,27 @@ - + Quantidade tributada sujeita a retenção. - + Alíquota ad rem do IBS sujeito a retenção - + Valor do IBS monofásico sujeito a retenção - + Alíquota ad rem da CBS sujeita a retenção - + Valor da CBS monofásica sujeita a retenção @@ -634,27 +766,27 @@ - + Quantidade tributada retida anteriormente - + Alíquota ad rem do IBS retido anteriormente - + Valor do IBS retido anteriormente - + Alíquota ad rem da CBS retida anteriormente - + Valor da CBS retida anteriormente @@ -668,22 +800,22 @@ - + Percentual do diferimento do imposto monofásico - + Valor do IBS monofásico diferido - + Percentual do diferimento do imposto monofásico - + Valor da CBS monofásica diferida @@ -691,12 +823,12 @@ - + Total de IBS monofásico do item - + Total da CBS monofásica do item @@ -711,7 +843,7 @@ IBS / CBS - + Valor do BC @@ -723,9 +855,9 @@ - + - Aliquota do IBS de competência das UF + Aliquota do IBS de competência das UF (em percentual) @@ -743,7 +875,7 @@ Grupo de campos da redução de aliquota - + Valor do IBS de competência das UF @@ -757,9 +889,9 @@ - + - Aliquota do IBS Municipal + Aliquota do IBS Municipal (em percentual) @@ -777,7 +909,7 @@ Grupo de campos da redução de aliquota - + Valor do IBS Municipal @@ -785,7 +917,7 @@ - + Valor do IBS @@ -797,9 +929,9 @@ - + - Aliquota da CBS + Aliquota da CBS (em percentual) @@ -817,7 +949,7 @@ Grupo de campos da redução de aliquota - + Valor da CBS @@ -830,16 +962,6 @@ Grupo de informações da Tributação Regular. Informar como seria a tributação caso não cumprida a condição resolutória/suspensiva. Exemplo 1: Art. 442, §4. Operações com ZFM e ALC. Exemplo 2: Operações com suspensão do tributo. - - - Grupo de Informações do Crédito Presumido referente ao IBS, quando aproveitado pelo emitente do documento. - - - - - Grupo de Informações do Crédito Presumido referente a CBS, quando aproveitado pelo emitente do documento. - - Grupo de informações da composição do valor do IBS e da CBS em compras governamental @@ -852,14 +974,14 @@ Tipo Redução Base de Cálculo - + Percentual de redução de aliquota do cClassTrib - + - Aliquota Efetiva que será aplicada a Base de Calculo + Aliquota Efetiva que será aplicada a Base de Calculo (em percentual) @@ -869,23 +991,18 @@ Tipo Crédito Presumido - - - Código de Classificação do Crédito Presumido do IBS e da CBS - - - + Percentual do Crédito Presumido - + Valor do Crédito Presumido - + Valor do Crédito Presumido Condição Suspensiva, preencher apenas para cCredPres que possui indicação de Condição Suspensiva @@ -898,12 +1015,12 @@ Tipo Diferimento - + Percentual do diferimento - + Valor do diferimento @@ -915,7 +1032,7 @@ Tipo Devolução Tributo - + Valor do tributo devolvido. No fornecimento de energia elétrica, água, esgoto e gás natural e em outras hipóteses definidas no regulamento @@ -939,37 +1056,37 @@ gás natural e em outras hipóteses definidas no regulamento Informar qual seria o cClassTrib caso não cumprida a condição resolutória/suspensiva - + Alíquota do IBS da UF Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva - + Valor do IBS da UF Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva - + Alíquota do IBS do Município Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva - + Valor do IBS do Município Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva - + Alíquota da CBS Informar como seria a Alíquota caso não cumprida a condição resolutória/suspensiva - + Valor da CBS Informar como seria o valor do Tributo caso não cumprida a condição resolutória/suspensiva @@ -982,20 +1099,20 @@ gás natural e em outras hipóteses definidas no regulamento Tipo Tributação Compra Governamental - - + + Valor que seria devido a UF, sem aplicação do Art. 473. da LC 214/2025 - - + + Valor que seria devido ao município, sem aplicação do Art. 473. da LC 214/2025 - - + + Valor que seria devido a CBS, sem aplicação do Art. 473. da LC 214/2025 @@ -1017,9 +1134,9 @@ gás natural e em outras hipóteses definidas no regulamento 4=Municípios - + - Percentual de redução de aliquota em compra goverrnamental + Percentual de redução de aliquota em compra governamental @@ -1039,9 +1156,9 @@ gás natural e em outras hipóteses definidas no regulamento 4=Municípios - + - Percentual de redução de aliquota em compra goverrnamental + Percentual de redução de aliquota em compra governamental @@ -1058,23 +1175,102 @@ gás natural e em outras hipóteses definidas no regulamento Tipo Transferência de Crédito - + Valor do IBS a ser transferido - + Valor da CBS a ser transferida + + + Tipo Estorno de Crédito + + + + + Valor do IBS a ser estornado + + + + + Valor da CBS a ser estornada + + + + + + + Ano e mês referência do período de apuração (AAAA-MM) + + + + + + + + Tipo Ajuste de Competência + + + + + Ano e mês referência do período de apuração (AAAA-MM) + + + + + Valor do IBS + + + + + Valor da CBS + + + + + + + Tipo Crédito Presumido da Operação + + + + + Valor da Base de Cálculo do Crédito Presumido da Operação + + + + + Código de Classificação do Crédito Presumido do IBS e da CBS + + + + + Grupo de Informações do Crédito Presumido referente ao IBS, quando aproveitado pelo emitente do documento. + + + + + Grupo de Informações do Crédito Presumido referente a CBS, quando aproveitado pelo emitente do documento. + + + + Tipo Informações do crédito presumido de IBS para fornecimentos a partir da ZFM + + + Ano e mês referência do período de apuração (AAAA-MM) + + Classificação de acordo com o art. 450, § 1º, da LC 214/25 para o cálculo do crédito presumido na ZFM @@ -1087,11 +1283,11 @@ OBS: Percentuais definidos no art. 450, § 1º, da LC 214/25 para o cálculo do - + Valor do crédito presumido calculado sobre o saldo devedor apurado - + \ No newline at end of file diff --git a/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd b/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd index 77067626..7d6fb69e 100644 --- a/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd +++ b/pynfe/data/XSDs/NF-e/leiauteNFe_v4.00.xsd @@ -94,6 +94,11 @@ SCAN 900-999 Data e Hora da saída ou de entrada da mercadoria / produto (AAAA-MM-DDTHH:mm:ssTZD) + + + Data da previsão de entrega ou disponibilização do bem (AAAA-MM-DD) + + Tipo do Documento Fiscal (0 - entrada; 1 - saída) @@ -204,13 +209,7 @@ Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do e - Tipo de Nota de Débito: -01=Transferência de créditos para Cooperativas; -02=Anulação de Crédito por Saídas Imunes/Isentas; -03=Débitos de notas fiscais não processadas na apuração; -04=Multa e juros; -05=Transferência de crédito de sucessão. - + Tipo de Nota de Débito @@ -993,6 +992,11 @@ Formato ”CFOP9999”. + + + Classificação para subapuração do IBS na ZFM + + Código EX TIPI (3 posições) @@ -7345,7 +7349,15 @@ alterado para tamanho variavel 1-4. (NT2011/004) - Tipo de Nota de Débito: 01=Transferência de créditos para Cooperativas; 02=Anulação de Crédito por Saídas Imunes/Isentas; 03=Débitos de notas fiscais não processadas na apuração; 04=Multa e juros; 05=Transferência de crédito de sucessão); 06=Pagamento antecipado; 07=Perda em estoque + Tipo de Nota de Débito: + 01=Transferência de créditos para Cooperativas; + 02=Anulação de Crédito por Saídas Imunes/Isentas; + 03=Débitos de notas fiscais não processadas na apuração; + 04=Multa e juros; + 05=Transferência de crédito na sucessão; + 06=Pagamento antecipado; + 07=Perda em estoque; + 08=Desenquadramento do SN; @@ -7356,17 +7368,26 @@ alterado para tamanho variavel 1-4. (NT2011/004) + - Tipo de Nota de Crédito: 01=Multa e juros; 02=Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM (art. 450, § 1º, LC 214/25); 03=Retorno + Tipo de Nota de Crédito: + 01=Multa e juros; + 02=Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM (art. 450, § 1º, LC 214/25); + 03=Retorno por recusa na entrega ou por não localização do destinatário na tentativa de entrega; + 04=Redução de valores; + 05=Transferência de crédito na sucessão; + + + From daecb573e19b2cf6c1e1cb013754aa0c2e0b0d45 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 15 Nov 2025 10:22:31 -0300 Subject: [PATCH 052/175] fix: update version to 0.5.6 in setup.py --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 222 +++++++++---------- setup.py | 2 +- 2 files changed, 112 insertions(+), 112 deletions(-) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index b0920da6..712b51cf 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -34,7 +34,7 @@ "tipoOperacao": "NFe/infNFe/ide/gCompraGov/tpOperGov" }, "pagAntecipado": [{ - "chave": "NFe/infNFe/ide/gPagAntecipado/refNFe" + "chave": "NFe/infNFe/ide/gPagAntecipado/refNFe[]" }], "emitente": { "cpfCnpj": "NFe/infNFe/emit/CNPJ", @@ -236,19 +236,19 @@ "reboque": [{ "vagao": "NFe/infNFe/transp/vagao", "balsa": "NFe/infNFe/transp/balsa", - "placa": "NFe/infNFe/transp/reboque/placa", - "uf": "NFe/infNFe/transp/reboque/UF", - "rntc": "NFe/infNFe/transp/reboque/RNTC" + "placa": "NFe/infNFe/transp/reboque[]/placa", + "uf": "NFe/infNFe/transp/reboque[]/UF", + "rntc": "NFe/infNFe/transp/reboque[]/RNTC" }], "volumes": [{ - "quantidade": "NFe/infNFe/transp/vol/qVol", - "especie": "NFe/infNFe/transp/vol/esp", - "marca": "NFe/infNFe/transp/vol/marca", - "numeracao": "NFe/infNFe/transp/vol/nVol", - "pesoLiquido": "NFe/infNFe/transp/vol/pesoL", - "pesoBruto": "NFe/infNFe/transp/vol/pesoB", + "quantidade": "NFe/infNFe/transp/vol[]/qVol", + "especie": "NFe/infNFe/transp/vol[]/esp", + "marca": "NFe/infNFe/transp/vol[]/marca", + "numeracao": "NFe/infNFe/transp/vol[]/nVol", + "pesoLiquido": "NFe/infNFe/transp/vol[]/pesoL", + "pesoBruto": "NFe/infNFe/transp/vol[]/pesoB", "lacres": [{ - "numero": "NFe/infNFe/transp/vol/lacres/nLacre" + "numero": "NFe/infNFe/transp/vol[]/lacres[]/nLacre" }] }] }, @@ -258,9 +258,9 @@ "valorDesconto": "NFe/infNFe/cobr/fat/vDesc", "valorLiquido": "NFe/infNFe/cobr/fat/vLiq", "parcelas": [{ - "numero": "NFe/infNFe/cobr/dup/nDup", - "dataVencimento": "NFe/infNFe/cobr/dup/dVenc", - "valor": "NFe/infNFe/cobr/dup/vDup" + "numero": "NFe/infNFe/cobr/dup[]/nDup", + "dataVencimento": "NFe/infNFe/cobr/dup[]/dVenc", + "valor": "NFe/infNFe/cobr/dup[]/vDup" }] }, "valorTroco": "NFe/infNFe/pag/vTroco", @@ -279,51 +279,51 @@ "cana": { "safra": "NFe/infNFe/cana/safra", "dataReferencia": "NFe/infNFe/cana/ref", + "quantidadeMes": "NFe/infNFe/cana/qTotMes", + "quantidadeAnterior": "NFe/infNFe/cana/qTotAnt", + "quantidadeGeral": "NFe/infNFe/cana/qTotGer", + "valorFornecimentos": "NFe/infNFe/cana/vFor", + "valorTotal": "NFe/infNFe/cana/vTotDed", + "valorLiquidoFornecimento": "NFe/infNFe/cana/vLiqFor", "fornecimentoDiario": [{ - "dia": "NFe/infNFe/cana/forDia/@dia", - "quantidadeKg": "NFe/infNFe/cana/forDia/qtde", - "quantidadeMes": "NFe/infNFe/cana/qTotMes", - "quantidadeAnterior": "NFe/infNFe/cana/qTotAnt", - "quantidadeGeral": "NFe/infNFe/cana/qTotGer" + "dia": "NFe/infNFe/cana/forDia[]/@dia", + "quantidadeKg": "NFe/infNFe/cana/forDia[]/qtde" }] }, "canaDeducoes": [{ - "descricao": "NFe/infNFe/cana/deduc/xDed", - "valor": "NFe/infNFe/cana/deduc/vDed", - "valorFornecimentos": "NFe/infNFe/cana/vFor", - "valorTotal": "NFe/infNFe/cana/vTotDed", - "valorLiquidoFornecimento": "NFe/infNFe/cana/vLiqFor" + "descricao": "NFe/infNFe/cana/deduc[]/xDed", + "valor": "NFe/infNFe/cana/deduc[]/vDed" }], "nfeReferenciada": [{ - "chave": "NFe/infNFe/ide/NFref/refNFe", - "chaveSigilo": "NFe/infNFe/ide/NFref/refNFeSig", + "chave": "NFe/infNFe/ide/NFref[]/refNFe", + "chaveSigilo": "NFe/infNFe/ide/NFref[]/refNFeSig", "nfePapel": { - "estado": "NFe/infNFe/ide/NFref/refNF/cUF", - "dataEmissao": "NFe/infNFe/ide/NFref/refNF/AAMM", - "cpfCnpj": "NFe/infNFe/ide/NFref/refNF/CNPJ", - "modelo": "NFe/infNFe/ide/NFref/refNF/mod", - "serie": "NFe/infNFe/ide/NFref/refNF/serie", - "numero": "NFe/infNFe/ide/NFref/refNF/nNF" + "estado": "NFe/infNFe/ide/NFref[]/refNF/cUF", + "dataEmissao": "NFe/infNFe/ide/NFref[]/refNF/AAMM", + "cpfCnpj": "NFe/infNFe/ide/NFref[]/refNF/CNPJ", + "modelo": "NFe/infNFe/ide/NFref[]/refNF/mod", + "serie": "NFe/infNFe/ide/NFref[]/refNF/serie", + "numero": "NFe/infNFe/ide/NFref[]/refNF/nNF" }, "produtorRural": { - "estado": "NFe/infNFe/ide/NFref/refNFP/cUF", - "dataEmissao": "NFe/infNFe/ide/NFref/refNFP/AAMM", - "cnpj": "NFe/infNFe/ide/NFref/refNFP/CNPJ", - "cpf": "NFe/infNFe/ide/NFref/refNFP/CPF", - "inscricaoEstadual": "NFe/infNFe/ide/NFref/refNFP/IE", - "modelo": "NFe/infNFe/ide/NFref/refNFP/mod", - "serie": "NFe/infNFe/ide/NFref/refNFP/serie", - "numero": "NFe/infNFe/ide/NFref/refNFP/nNF" + "estado": "NFe/infNFe/ide/NFref[]/refNFP/cUF", + "dataEmissao": "NFe/infNFe/ide/NFref[]/refNFP/AAMM", + "cnpj": "NFe/infNFe/ide/NFref[]/refNFP/CNPJ", + "cpf": "NFe/infNFe/ide/NFref[]/refNFP/CPF", + "inscricaoEstadual": "NFe/infNFe/ide/NFref[]/refNFP/IE", + "modelo": "NFe/infNFe/ide/NFref[]/refNFP/mod", + "serie": "NFe/infNFe/ide/NFref[]/refNFP/serie", + "numero": "NFe/infNFe/ide/NFref[]/refNFP/nNF" }, - "chaveCte": "NFe/infNFe/ide/NFref/refCTe", + "chaveCte": "NFe/infNFe/ide/NFref[]/refCTe", "cupomFiscal": { - "modelo": "NFe/infNFe/ide/NFref/refECF/mod", - "numeroOrdemSequencia": "NFe/infNFe/ide/NFref/refECF/nECF", - "numeroContador": "NFe/infNFe/ide/NFref/refECF/nCOO" + "modelo": "NFe/infNFe/ide/NFref[]/refECF/mod", + "numeroOrdemSequencia": "NFe/infNFe/ide/NFref[]/refECF/nECF", + "numeroContador": "NFe/infNFe/ide/NFref[]/refECF/nCOO" } }], "responsavelAutorizado": [{ - "cpfCnpj": "NFe/infNFe/autXML/CNPJ" + "cpfCnpj": "NFe/infNFe/autXML[]/CNPJ" }], "itens": [{ "codigo": "NFe/infNFe/det/prod/cProd", @@ -336,9 +336,9 @@ "cnpjFabricante": "NFe/infNFe/det/prod/CNPJFab", "codigoBeneficioFiscal": "NFe/infNFe/det/prod/cBenef", "creditoPresumido": [{ - "codigo": "NFe/infNFe/det/prod/gCred/cCredPresumido", - "percentual": "NFe/infNFe/det/prod/gCred/pCredPresumido", - "valor": "NFe/infNFe/det/prod/gCred/vCredPresumido" + "codigo": "NFe/infNFe/det/prod/gCred[]/cCredPresumido", + "percentual": "NFe/infNFe/det/prod/gCred[]/pCredPresumido", + "valor": "NFe/infNFe/det/prod/gCred[]/vCredPresumido" }], "exTipi": "NFe/infNFe/det/prod/EXTIPI", "cfop": "NFe/infNFe/det/prod/CFOP", @@ -394,9 +394,9 @@ "restricao": "NFe/infNFe/det/prod/veicProd/tpRest" }, "medicamentos": [{ - "codigoAnvisa": "NFe/infNFe/det/prod/med/cProdANVISA", - "motivoInsencaoAnvisa": "NFe/infNFe/det/prod/med/xMotivoIsencao", - "valorMaximo": "NFe/infNFe/det/prod/med/vPMC" + "codigoAnvisa": "NFe/infNFe/det/prod/med[]/cProdANVISA", + "motivoInsencaoAnvisa": "NFe/infNFe/det/prod/med[]/xMotivoIsencao", + "valorMaximo": "NFe/infNFe/det/prod/med[]/vPMC" }], "combustivel": { "codigoAnp": "NFe/infNFe/det/prod/comb/cProdANP", @@ -422,9 +422,9 @@ "valorFinal": "NFe/infNFe/det/prod/comb/encerrante/vEncFin" }, "origemCombustivel": [{ - "indicadorImportacao": "NFe/infNFe/det/prod/comb/origComb/indImport", - "codigoUf": "NFe/infNFe/det/prod/comb/origComb/cUFOrig", - "percentualOrigUf": "NFe/infNFe/det/prod/comb/origComb/pOrig" + "indicadorImportacao": "NFe/infNFe/det/prod/comb/origComb[]/indImport", + "codigoUf": "NFe/infNFe/det/prod/comb/origComb[]/cUFOrig", + "percentualOrigUf": "NFe/infNFe/det/prod/comb/origComb[]/pOrig" }] }, "papelImune": { @@ -987,61 +987,61 @@ }, "informacoesComplementares": "NFe/infNFe/det/infAdProd", "numero": "NFe/infNFe/det/@nItem", - "nve": [{"numero": "NFe/infNFe/det/prod/NVE"}], + "nve": [{"numero": "NFe/infNFe/det/prod/NVE[]"}], "importacao": [{ - "numero": "NFe/infNFe/det/prod/DI/nDI", - "dataEmissao": "NFe/infNFe/det/prod/DI/dDI", + "numero": "NFe/infNFe/det/prod/DI[]/nDI", + "dataEmissao": "NFe/infNFe/det/prod/DI[]/dDI", "desembaraco": { - "local": "NFe/infNFe/det/prod/DI/xLocDesemb", - "estado": "NFe/infNFe/det/prod/DI/UFDesemb", - "data": "NFe/infNFe/det/prod/DI/dDesemb" + "local": "NFe/infNFe/det/prod/DI[]/xLocDesemb", + "estado": "NFe/infNFe/det/prod/DI[]/UFDesemb", + "data": "NFe/infNFe/det/prod/DI[]/dDesemb" }, - "viaTransporte": "NFe/infNFe/det/prod/DI/tpViaTransp", - "valorAfrmm": "NFe/infNFe/det/prod/DI/vAFRMM", - "formaImportacao": "NFe/infNFe/det/prod/DI/tpIntermedio", + "viaTransporte": "NFe/infNFe/det/prod/DI[]/tpViaTransp", + "valorAfrmm": "NFe/infNFe/det/prod/DI[]/vAFRMM", + "formaImportacao": "NFe/infNFe/det/prod/DI[]/tpIntermedio", "adquirente": { - "cnpj": "NFe/infNFe/det/prod/DI/CNPJ", - "cpf": "NFe/infNFe/det/prod/DI/CPF", - "estado": "NFe/infNFe/det/prod/DI/UFTerceiro" + "cnpj": "NFe/infNFe/det/prod/DI[]/CNPJ", + "cpf": "NFe/infNFe/det/prod/DI[]/CPF", + "estado": "NFe/infNFe/det/prod/DI[]/UFTerceiro" }, - "codigoExportador": "NFe/infNFe/det/prod/DI/cExportador", + "codigoExportador": "NFe/infNFe/det/prod/DI[]/cExportador", "adicoes": [{ - "numero": "NFe/infNFe/det/prod/DI/adi/nAdicao", - "numeroSequencia": "NFe/infNFe/det/prod/DI/adi/nSeqAdic", - "codigoFabricante": "NFe/infNFe/det/prod/DI/adi/cFabricante", - "valorDesconto": "NFe/infNFe/det/prod/DI/adi/vDescDI", - "numeroDrawback": "NFe/infNFe/det/prod/DI/adi/nDraw" + "numero": "NFe/infNFe/det/prod/DI[]/adi[]/nAdicao", + "numeroSequencia": "NFe/infNFe/det/prod/DI[]/adi[]/nSeqAdic", + "codigoFabricante": "NFe/infNFe/det/prod/DI[]/adi[]/cFabricante", + "valorDesconto": "NFe/infNFe/det/prod/DI[]/adi[]/vDescDI", + "numeroDrawback": "NFe/infNFe/det/prod/DI[]/adi[]/nDraw" }] }], "exportacao": [{ - "numeroDrawback": "NFe/infNFe/det/prod/detExport/nDraw", + "numeroDrawback": "NFe/infNFe/det/prod/detExport[]/nDraw", "exportacaoIndireta": { - "numero": "NFe/infNFe/det/prod/detExport/exportInd/nRE", - "chave": "NFe/infNFe/det/prod/detExport/exportInd/chNFe", - "quantidade": "NFe/infNFe/det/prod/detExport/exportInd/qExport" + "numero": "NFe/infNFe/det/prod/detExport[]/exportInd/nRE", + "chave": "NFe/infNFe/det/prod/detExport[]/exportInd/chNFe", + "quantidade": "NFe/infNFe/det/prod/detExport[]/exportInd/qExport" } }], "rastreavel": [{ - "lote": "NFe/infNFe/det/prod/rastro/nLote", - "quantidade": "NFe/infNFe/det/prod/rastro/qLote", - "dataFabricacao": "NFe/infNFe/det/prod/rastro/dFab", - "dataValidade": "NFe/infNFe/det/prod/rastro/dVal", - "codigoAgregacao": "NFe/infNFe/det/prod/rastro/cAgreg" + "lote": "NFe/infNFe/det/prod/rastro[]/nLote", + "quantidade": "NFe/infNFe/det/prod/rastro[]/qLote", + "dataFabricacao": "NFe/infNFe/det/prod/rastro[]/dFab", + "dataValidade": "NFe/infNFe/det/prod/rastro[]/dVal", + "codigoAgregacao": "NFe/infNFe/det/prod/rastro[]/cAgreg" }], "armamentos": [{ - "tipo": "NFe/infNFe/det/prod/arma/tpArma", - "numeroSerie": "NFe/infNFe/det/prod/arma/nSerie", - "numeroSerieCano": "NFe/infNFe/det/prod/arma/nCano", - "descricao": "NFe/infNFe/det/prod/arma/descr" + "tipo": "NFe/infNFe/det/prod/arma[]/tpArma", + "numeroSerie": "NFe/infNFe/det/prod/arma[]/nSerie", + "numeroSerieCano": "NFe/infNFe/det/prod/arma[]/nCano", + "descricao": "NFe/infNFe/det/prod/arma[]/descr" }], "observacoes": { "contribuinte": [{ - "campo": "NFe/infNFe/det/obsItem/obsCont/@xCampo", - "texto": "NFe/infNFe/det/obsItem/obsCont/xTexto" + "campo": "NFe/infNFe/det/obsItem/obsCont[]/@xCampo", + "texto": "NFe/infNFe/det/obsItem/obsCont[]/xTexto" }], "fisco": [{ - "texto": "NFe/infNFe/det/obsItem/obsFisco/@xCampo", - "campo": "NFe/infNFe/det/obsItem/obsFisco/xTexto" + "texto": "NFe/infNFe/det/obsItem/obsFisco[]/@xCampo", + "campo": "NFe/infNFe/det/obsItem/obsFisco[]/xTexto" }] }, "valorTotal": "NFe/infNFe/det/vItem", @@ -1051,24 +1051,24 @@ } }], "pagamentos": [{ - "aVista": "NFe/infNFe/pag/detPag/indPag", - "meio": "NFe/infNFe/pag/detPag/tPag", - "descricaoMeio": "NFe/infNFe/pag/detPag/xPag", - "valor": "NFe/infNFe/pag/detPag/vPag", + "aVista": "NFe/infNFe/pag/detPag[]/indPag", + "meio": "NFe/infNFe/pag/detPag[]/tPag", + "descricaoMeio": "NFe/infNFe/pag/detPag[]/xPag", + "valor": "NFe/infNFe/pag/detPag[]/vPag", "cartao": { - "tipoIntegracao": "NFe/infNFe/pag/detPag/card/tpIntegra", - "cnpjCredenciadora": "NFe/infNFe/pag/detPag/card/CNPJ", - "bandeiraOperadora": "NFe/infNFe/pag/detPag/card/tBand", - "numeroAutorizacao": "NFe/infNFe/pag/detPag/card/cAut" + "tipoIntegracao": "NFe/infNFe/pag/detPag[]/card/tpIntegra", + "cnpjCredenciadora": "NFe/infNFe/pag/detPag[]/card/CNPJ", + "bandeiraOperadora": "NFe/infNFe/pag/detPag[]/card/tBand", + "numeroAutorizacao": "NFe/infNFe/pag/detPag[]/card/cAut" } }], "pagamento": { - "data": "NFe/infNFe/pag/detPag/dPag", - "cnpjTransacional": "NFe/infNFe/pag/detPag/CNPJPag", - "ufTransacional": "NFe/infNFe/pag/detPag/UFPag", + "data": "NFe/infNFe/pag/detPag[]/dPag", + "cnpjTransacional": "NFe/infNFe/pag/detPag[]/CNPJPag", + "ufTransacional": "NFe/infNFe/pag/detPag[]/UFPag", "cartao": { - "cnpjBeneficiario": "NFe/infNFe/pag/detPag/card/CNPJReceb", - "idTerminal": "NFe/infNFe/pag/detPag/card/idTermPag" + "cnpjBeneficiario": "NFe/infNFe/pag/detPag[]/card/CNPJReceb", + "idTerminal": "NFe/infNFe/pag/detPag[]/card/idTermPag" } }, "intermediadorTransacao": { @@ -1077,18 +1077,18 @@ }, "observacoes": { "contribuinte": [{ - "campo": "NFe/infNFe/infAdic/obsCont/@xCampo", - "texto": "NFe/infNFe/infAdic/obsCont/xTexto" + "campo": "NFe/infNFe/infAdic/obsCont[]/@xCampo", + "texto": "NFe/infNFe/infAdic/obsCont[]/xTexto" }], "fisco": [{ - "texto": "NFe/infNFe/infAdic/obsFisco/xTexto", - "campo": "NFe/infNFe/infAdic/obsFisco/@xCampo" + "texto": "NFe/infNFe/infAdic/obsFisco[]/xTexto", + "campo": "NFe/infNFe/infAdic/obsFisco[]/@xCampo" }] }, "processoReferenciado": [{ - "identificador": "NFe/infNFe/infAdic/procRef/nProc", - "origem": "NFe/infNFe/infAdic/procRef/indProc", - "tipo": "NFe/infNFe/infAdic/procRef/tpAto" + "identificador": "NFe/infNFe/infAdic/procRef[]/nProc", + "origem": "NFe/infNFe/infAdic/procRef[]/indProc", + "tipo": "NFe/infNFe/infAdic/procRef[]/tpAto" }], "responsavelTecnico": { "cpfCnpj": "NFe/infNFe/infRespTec/CNPJ", @@ -1100,8 +1100,8 @@ }, "agropecuario": { "defensivo": [{ - "numeroReceituario": "NFe/infNFe/agropecuario/defensivo/nReceituario", - "cpfResponsavelTecnico": "NFe/infNFe/agropecuario/defensivo/CPFRespTec" + "numeroReceituario": "NFe/infNFe/agropecuario/defensivo[]/nReceituario", + "cpfResponsavelTecnico": "NFe/infNFe/agropecuario/defensivo[]/CPFRespTec" }], "guiaTransito": { "tipoGuia": "NFe/infNFe/agropecuario/guiaTransito/tpGuia", diff --git a/setup.py b/setup.py index 922ca168..21b209f7 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.5", + version="0.5.6", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From b9a4502f089fb4ccb7c9efa93617f462959c6fb6 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 28 Nov 2025 14:06:27 -0300 Subject: [PATCH 053/175] =?UTF-8?q?fix:=20corrige=20nome=20do=20campo=20'v?= =?UTF-8?q?alorIbsCreditoPresumindo'=20para=20'valorIbsCreditoPresumido'?= =?UTF-8?q?=20no=20schema=20NF-e=20fix:=20atualiza=20vers=C3=A3o=20para=20?= =?UTF-8?q?0.5.7=20no=20setup.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index 712b51cf..60af2dc9 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -191,7 +191,7 @@ "valorDevolucaoIbsMunicipio": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSMun/vDevTrib", "valorIbsMunicipio": "NFe/infNFe/total/IBSCBSTot/gIBS/gIBSMun/vIBSMun", "valorIbs": "NFe/infNFe/total/IBSCBSTot/gIBS/vIBS", - "valorIbsCreditoPresumindo": "NFe/infNFe/total/IBSCBSTot/gIBS/vCredPres", + "valorIbsCreditoPresumido": "NFe/infNFe/total/IBSCBSTot/gIBS/vCredPres", "valorIbsCreditoPresumidoSupensao": "NFe/infNFe/total/IBSCBSTot/gIBS/vCredPresCondSus", "valorCbsDiferimento": "NFe/infNFe/total/IBSCBSTot/gCBS/vDif", "valorCbsDevolucao": "NFe/infNFe/total/IBSCBSTot/gCBS/vDevTrib", diff --git a/setup.py b/setup.py index 21b209f7..804dd008 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.6", + version="0.5.7", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From ff9b0976bc9b086cb616c90e7386d546ea597955 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 28 Nov 2025 14:15:54 -0300 Subject: [PATCH 054/175] fix: corrige nome do campo 'tipoCreditoPresumindoIbsZFM' para 'tipoCreditoPresumidoIbsZFM' no schema NF-e --- pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json index 60af2dc9..10b3df7e 100644 --- a/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json +++ b/pynfe/data/SCHEMAs/NF-e/schemaNFe_v1.00.json @@ -362,7 +362,7 @@ "valorDesconto": "NFe/infNFe/det/prod/vDesc", "valorOutros": "NFe/infNFe/det/prod/vOutro", "compoeTotal": "NFe/infNFe/det/prod/indTot", - "tipoCreditoPresumindoIbsZFM": "NFe/infNFe/det/prod/tpCredPresIBSZFM", + "tipoCreditoPresumidoIbsZFM": "NFe/infNFe/det/prod/tpCredPresIBSZFM", "indicadorBemMovelUsado": "NFe/infNFe/det/prod/indBemMovelUsado", "numeroCompra": "NFe/infNFe/det/prod/xPed", "pedidoCompra": "NFe/infNFe/det/prod/nItemPed", From b8dea9da97fef98c2626cc0dcb7a0913442cdd6f Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Tue, 2 Dec 2025 22:33:56 -0300 Subject: [PATCH 055/175] wip --- pynfe/processamento/comunicacao.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ba0d7314..29cb1d4a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -974,7 +974,9 @@ def _post_sp_https(self, url, xml, metodo): # gerar nfse if metodo == "enviar_rps": - return cliente.service.EnviarRPS(VersaoSchema=1, MensagemXML=xml) + return cliente.service.EnvioRPS(VersaoSchema=1, MensagemXML=xml) + if metodo == "teste_envio_lote_rps": + return cliente.service.TesteEnvioLoteRPS(VersaoSchema=1, MensagemXML=xml) if metodo == "consultar_rps": return cliente.service.ConsultaNFe(VersaoSchema=1, MensagemXML=xml) elif metodo == "cancelar": From b82a696cb5c06b4ba63f739ac7ad2f7fd301f33a Mon Sep 17 00:00:00 2001 From: LucasBonna Date: Fri, 12 Dec 2025 14:09:37 -0300 Subject: [PATCH 056/175] =?UTF-8?q?feat:=20enhance=20ComunicacaoNfse=20cla?= =?UTF-8?q?ss=20for=20S=C3=A3o=20Paulo=20NFSe=20communication=20by=20addin?= =?UTF-8?q?g=20schema=20version=20support=20and=20new=20methods=20for=20RP?= =?UTF-8?q?S=20operations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 49 ++++++++++++++++++++++-------- pynfe/utils/webservices.py | 6 +++- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 29cb1d4a..369715e2 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -953,16 +953,37 @@ def _post_barueri_https(self, url, xml, metodo): finally: certificadoA1.excluir() - def enviar_sp(self, xml, operation): + def enviar_sp(self, xml, operation, versao_schema=2): + """ + Send XML to São Paulo NFS-e webservice. + + Args: + xml: XML string to send + operation: Operation name (enviar_rps, teste_envio_lote_rps, envio_lote_rps, consultar_rps, cancelar) + versao_schema: Schema version (1 for v1, 2 for v2 - Reforma Tributária 2026) + + Returns: + WebService response + """ url = self._get_url() if self.autorizador == "SAO_PAULO": - return self._post_sp_https(url, xml, operation) + return self._post_sp_https(url, xml, operation, versao_schema) else: raise Exception(f"Enviar RPS não implementado para {self.autorizador}") - def _post_sp_https(self, url, xml, metodo): - """Comunicação wsdl (https) utilizando certificado do usuário""" - # comunicacao wsdl + def _post_sp_https(self, url, xml, metodo, versao_schema=2): + """ + Comunicação wsdl (https) utilizando certificado do usuário. + + According to São Paulo NFS-e manual v3.3.4: + - VersaoSchema=2: Schema version 2 (Reforma Tributária 2026) + + Args: + url: WebService URL + xml: XML message string + metodo: Method name + versao_schema: Schema version (1 or 2) + """ certificadoA1 = CertificadoA1(self.certificado) try: from pynfe.utils.https_nfse import HttpAuthenticated @@ -972,16 +993,18 @@ def _post_sp_https(self, url, xml, metodo): cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) - # gerar nfse + # São Paulo NFS-e WebService methods + # Manual v3.3.4 section 4.3.1: All methods receive VersaoSchema and MensagemXML if metodo == "enviar_rps": - return cliente.service.EnvioRPS(VersaoSchema=1, MensagemXML=xml) - if metodo == "teste_envio_lote_rps": - return cliente.service.TesteEnvioLoteRPS(VersaoSchema=1, MensagemXML=xml) - if metodo == "consultar_rps": - return cliente.service.ConsultaNFe(VersaoSchema=1, MensagemXML=xml) + return cliente.service.EnvioRPS(VersaoSchema=versao_schema, MensagemXML=xml) + elif metodo == "teste_envio_lote_rps": + return cliente.service.TesteEnvioLoteRPS(VersaoSchema=versao_schema, MensagemXML=xml) + elif metodo == "envio_lote_rps": + return cliente.service.EnvioLoteRPS(VersaoSchema=versao_schema, MensagemXML=xml) + elif metodo == "consultar_rps": + return cliente.service.ConsultaNFe(VersaoSchema=versao_schema, MensagemXML=xml) elif metodo == "cancelar": - return cliente.service.CancelamentoNFe(VersaoSchema=1, MensagemXML=xml) - # TODO outros metodos + return cliente.service.CancelamentoNFe(VersaoSchema=versao_schema, MensagemXML=xml) else: raise Exception(f"Método {metodo} não implementado no autorizador São Paulo.") except Exception as e: diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 4cccb546..8c675737 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -502,9 +502,13 @@ # "SAO_PAULO": { "ENVIAR_RPS": "EnviarRps", + "ENVIO_LOTE_RPS": "EnvioLoteRPS", + "TESTE_ENVIO_LOTE_RPS": "TesteEnvioLoteRPS", "CONSULTA_RPS": "ConsultaNFe", "CANCELAR_NFSE": "CancelamentoNFe", - "HTTPS": "https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl" + # New URL supports both v1 and v2 (Reforma Tributária 2026) + # Old URL (v1 only): https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl + "HTTPS": "https://nfews.prefeitura.sp.gov.br/lotenfe.asmx?WSDL" }, "BARUERI": { "ENVIAR_RPS": "EnviarRPS", From f2cea7f996e942648a21c4546d28f46149d22a66 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 13 Dec 2025 18:50:02 -0300 Subject: [PATCH 057/175] =?UTF-8?q?feat:=20adiciona=20suporte=20ao=20autor?= =?UTF-8?q?izador=20Osasco=20com=20m=C3=A9todos=20de=20consulta=20e=20canc?= =?UTF-8?q?elamento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 27 ++++++++++ pynfe/processamento/comunicacao.py | 69 ++++++++++++++++++++----- pynfe/utils/webservices.py | 9 ++++ setup.py | 3 +- 4 files changed, 94 insertions(+), 14 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 6184077e..fe6232e0 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -10,6 +10,33 @@ def consultar_rps(self): def cancelar(self): pass +class SerializacaoOsasco: + def __init__(self, chave_autenticacao): + self.chave_autenticacao = chave_autenticacao + + def _consultar(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, numero_rps_unico=None): + return { + "ChaveAutenticacao": self.chave_autenticacao, + "CNPJTomador": cnpj_tomador, + "CPFTomador": cpf_tomador, + "DataInicial": data_inicial, + "DataFinal": data_final, + "NumeroNotaInicial": numero_nota_inicial, + "NumeroNotaFinal": numero_nota_final, + "NumeroReciboInicial": numero_rps_inicial, + "NumeroReciboFinal": numero_rps_final, + "NumeroReciboUnico": numero_rps_unico, + } + + def consultar_periodo(self, data_inicial, data_final, cnpj_tomador=None, cpf_tomador=None): + return self._consultar(data_inicial=data_inicial, data_final=data_final, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) + + def consultar_faixa(self,numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, cnpj_tomador=None, cpf_tomador=None): + return self._consultar(numero_nota_inicial=numero_nota_inicial, numero_nota_final=numero_nota_final, numero_rps_inicial=numero_rps_inicial, numero_rps_final=numero_rps_final, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) + + def consultar_nota(self, numero_nota, cnpj_tomador=None, cpf_tomador=None): + return self._consultar(numero_nota_inicial=numero_nota, numero_nota_final=numero_nota, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 713d4bc3..485b4d7c 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -610,14 +610,14 @@ def _post(self, url, xml, timeout=None): finally: certificado_a1.excluir() - class ComunicacaoNfse(Comunicacao): """Classe de comunicação que segue o padrão definido para as SEFAZ dos Municípios.""" _versao = "" _namespace = "" + - def __init__(self, certificado, certificado_senha, autorizador, homologacao=False): + def __init__(self, autorizador, certificado=None, certificado_senha=None, homologacao=False): self.certificado = certificado self.certificado_senha = certificado_senha self._ambiente = 2 if homologacao else 1 @@ -628,6 +628,9 @@ def __init__(self, certificado, certificado_senha, autorizador, homologacao=Fals elif self.autorizador == "BETHA": self._namespace = NAMESPACE_BETHA self._versao = "2.02" + elif self.autorizador == "OSASCO": + self._namespace = "" + self._versao = "1" else: raise Exception("Autorizador não encontrado!") @@ -653,16 +656,19 @@ def enviar_lote(self, xml): else: raise Exception("Este método só esta implementado no autorizador ginfes.") - def consultar(self, xml): + def consultar(self, payload): # url do serviço url = self._get_url() if self.autorizador == "GINFES": # xml - xml = '' + xml + payload = '' + payload + # comunica via wsdl + return self._post_https(url, payload, "consulta") + elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_https(url, xml, "consulta") + return self._zeep_client(url, payload, "ConsultarNotaCompleta") else: - raise Exception("Este método só esta implementado no autorizador ginfes.") + raise Exception("Este método não esta implementado para o autorizador.") def consultar_rps(self, xml): # url do serviço @@ -672,9 +678,11 @@ def consultar_rps(self, xml): return self._post(url, xml, "consultaRps") elif self.autorizador == "GINFES": return self._post_https(url, xml, "consultaRps") - # TODO outros autorizadres + elif self.autorizador == "OSASCO": + # comunica via wsdl + return self._zeep_client(url, xml, "Consultar") else: - raise Exception("Autorizador não encontrado!") + raise Exception("Este método não esta implementado para o autorizador.") def consultar_faixa(self, xml): # url do serviço @@ -682,8 +690,11 @@ def consultar_faixa(self, xml): if self.autorizador == "BETHA": # comunica via wsdl return self._post(url, xml, "consultaFaixa") + elif self.autorizador == "OSASCO": + # comunica via wsdl + return self._zeep_client(url, xml, "Consultar") else: - raise Exception("Este método só esta implementado no autorizador betha.") + raise Exception("Este método não esta implementado para o autorizador.") def consultar_lote(self, xml): # url do serviço @@ -694,7 +705,7 @@ def consultar_lote(self, xml): # comunica via wsdl return self._post_https(url, xml, "consulta_lote") else: - raise Exception("Este método só esta implementado no autorizador ginfes.") + raise Exception("Este método não esta implementado para o autorizador.") def consultar_situacao_lote(self, xml): # url do serviço @@ -703,7 +714,7 @@ def consultar_situacao_lote(self, xml): # comunica via wsdl return self._post_https(url, xml, "consulta_situacao_lote") else: - raise Exception("Este método só esta implementado no autorizador ginfes.") + raise Exception("Este método não esta implementado para o autorizador.") def cancelar(self, xml): # url do serviço @@ -716,9 +727,9 @@ def cancelar(self, xml): elif self.autorizador == "GINFES": # comunica via wsdl com certificado return self._post_https(url, xml, "cancelar") - # TODO outros autorizadres + # TODO outros autorizadores else: - raise Exception("Autorizador não encontrado!") + raise Exception("Este método não esta implementado para o autorizador.") def _cabecalho(self, retorna_string=True): """Monta o XML do cabeçalho da requisição wsdl @@ -841,7 +852,39 @@ def _post_https(self, url, xml, metodo): raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + + def _zeep_client(self, wsdl, payload, metodo, wcf_compatibility=True): + """Comunicação wsdl utilizando a biblioteca zeep""" + + # comunicacao wsdl + try: + from zeep import Client + from zeep.transports import Transport + from zeep.settings import Settings + session = requests.Session() + + transport = Transport( + session=session, + timeout=60 + ) + + settings = Settings( + strict=not wcf_compatibility, # IMPORTANTÍSSIMO p/ WCF + xml_huge_tree=True + ) + client = Client( + wsdl=wsdl, + transport=transport, + settings=settings + ) + if hasattr(client.service, metodo): + service = getattr(client.service, metodo) + return service(payload) + else: + raise Exception("Método não implementado no autorizador.") + except Exception as e: + raise e class ComunicacaoMDFe(Comunicacao): MDFE_SITUACAO_JA_ENVIADO = ("100", "101", "132") diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 17e96716..358ba425 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -525,6 +525,15 @@ "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl", "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl", }, + "OSASCO": { + "AUTORIZACAO": "Emitir", + "AUTORIZACAO_LOTE": "EmitirEmLote", + "CANCELAR": "Cancelar", + "CANCELAR_LOTE": "CancelarNotaLote", + "CONSULTA": "Consultar", + "HTTPS": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", + "HOMOLOGACAO": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", + }, } # MDF-e diff --git a/setup.py b/setup.py index 804dd008..2f6d6aa7 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.5.7", + version="0.6.0", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", @@ -34,6 +34,7 @@ "requests", "lxml", "signxml", + "zeep>=4.3.2", ], extras_require={ "nfse": [ From c13460a6d58912970d9bcb08c1f5670481d54ac5 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sat, 13 Dec 2025 20:03:09 -0300 Subject: [PATCH 058/175] =?UTF-8?q?feat:=20atualiza=20a=20classe=20Seriali?= =?UTF-8?q?zacaoOsasco=20para=20renomear=20m=C3=A9todo=20de=20consulta=20e?= =?UTF-8?q?=20adiciona=20novo=20m=C3=A9todo=20de=20consulta=20na=20classe?= =?UTF-8?q?=20SerializacaoNfse=20fix:=20atualiza=20vers=C3=A3o=20para=200.?= =?UTF-8?q?6.1=20no=20setup.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 12 +----------- pynfe/processamento/serializacao.py | 11 ++++++++++- setup.py | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index fe6232e0..1416ddc7 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -14,7 +14,7 @@ class SerializacaoOsasco: def __init__(self, chave_autenticacao): self.chave_autenticacao = chave_autenticacao - def _consultar(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, numero_rps_unico=None): + def consultar(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, numero_rps_unico=None): return { "ChaveAutenticacao": self.chave_autenticacao, "CNPJTomador": cnpj_tomador, @@ -28,16 +28,6 @@ def _consultar(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, dat "NumeroReciboUnico": numero_rps_unico, } - def consultar_periodo(self, data_inicial, data_final, cnpj_tomador=None, cpf_tomador=None): - return self._consultar(data_inicial=data_inicial, data_final=data_final, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) - - def consultar_faixa(self,numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, cnpj_tomador=None, cpf_tomador=None): - return self._consultar(numero_nota_inicial=numero_nota_inicial, numero_nota_final=numero_nota_final, numero_rps_inicial=numero_rps_inicial, numero_rps_final=numero_rps_final, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) - - def consultar_nota(self, numero_nota, cnpj_tomador=None, cpf_tomador=None): - return self._consultar(numero_nota_inicial=numero_nota, numero_nota_final=numero_nota, cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador) - - class SerializacaoBetha(InterfaceAutorizador): def __init__(self): # importa diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 95cd329e..6a4294e0 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2206,9 +2206,10 @@ def gerar_qrcode(self, token, csc, xml, return_qr=False, online=True): class SerializacaoNfse(object): - def __init__(self, autorizador): + def __init__(self, autorizador, chave_autenticacao=None): "Recebe uma string com o nome do autorizador." self.autorizador = autorizador + self.chave_autenticacao = chave_autenticacao def gerar(self, nfse): if self.autorizador.lower() == "betha": @@ -2233,6 +2234,14 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): return SerializacaoGinfes().consultar_nfse(emitente, numero, inicio, fim) else: raise Exception("Este método só esta implementado no autorizador ginfes.") + + def consultar_nota_emitida(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None): + if self.autorizador.lower() == "osasco": + from pynfe.processamento.autorizador_nfse import SerializacaoOsasco + + return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador, data_inicial=data_inicial, data_final=data_final, numero_nota_inicial=numero_nota_inicial, numero_nota_final=numero_nota_final, numero_rps_inicial=numero_rps_inicial, numero_rps_final=numero_rps_final) + else: + raise Exception("Este método só esta implementado no autorizador Osasco.") def consultar_lote(self, emitente, numero): if self.autorizador.lower() == "ginfes": diff --git a/setup.py b/setup.py index 2f6d6aa7..c5f5caec 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.6.0", + version="0.6.1", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From cdb844fe9d678d452b2bcb86f524cf614eeabf60 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 14 Dec 2025 13:39:03 -0300 Subject: [PATCH 059/175] =?UTF-8?q?feat:=20atualiza=20a=20vers=C3=A3o=20pa?= =?UTF-8?q?ra=200.6.2=20no=20setup.py=20e=20refatora=20a=20classe=20Comuni?= =?UTF-8?q?cacaoNfse=20para=20melhorar=20a=20legibilidade=20do=20c=C3=B3di?= =?UTF-8?q?go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 31 +++++++++++++----------------- setup.py | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 485b4d7c..5dc1c4f8 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -610,12 +610,12 @@ def _post(self, url, xml, timeout=None): finally: certificado_a1.excluir() + class ComunicacaoNfse(Comunicacao): """Classe de comunicação que segue o padrão definido para as SEFAZ dos Municípios.""" _versao = "" _namespace = "" - def __init__(self, autorizador, certificado=None, certificado_senha=None, homologacao=False): self.certificado = certificado @@ -852,40 +852,35 @@ def _post_https(self, url, xml, metodo): raise Exception("Método não implementado no autorizador.") except Exception as e: raise e - + def _zeep_client(self, wsdl, payload, metodo, wcf_compatibility=True): """Comunicação wsdl utilizando a biblioteca zeep""" - - # comunicacao wsdl + + # comunicacao wsdl try: - from zeep import Client - from zeep.transports import Transport + from zeep import Client + from zeep.helpers import serialize_object from zeep.settings import Settings + from zeep.transports import Transport + session = requests.Session() - transport = Transport( - session=session, - timeout=60 - ) + transport = Transport(session=session, timeout=60) settings = Settings( - strict=not wcf_compatibility, # IMPORTANTÍSSIMO p/ WCF - xml_huge_tree=True + strict=not wcf_compatibility, xml_huge_tree=True # IMPORTANTÍSSIMO p/ WCF ) - client = Client( - wsdl=wsdl, - transport=transport, - settings=settings - ) + client = Client(wsdl=wsdl, transport=transport, settings=settings) if hasattr(client.service, metodo): service = getattr(client.service, metodo) - return service(payload) + return serialize_object(service(payload)) else: raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + class ComunicacaoMDFe(Comunicacao): MDFE_SITUACAO_JA_ENVIADO = ("100", "101", "132") diff --git a/setup.py b/setup.py index c5f5caec..4a356cd4 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.6.1", + version="0.6.2", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 7361d854fa934846d40647e8db42374b15a54a47 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 09:48:30 -0300 Subject: [PATCH 060/175] =?UTF-8?q?feat:=20adiciona=20suporte=20ao=20NFCOM?= =?UTF-8?q?=20com=20m=C3=A9todos=20de=20consulta=20e=20atualiza=20a=20vers?= =?UTF-8?q?=C3=A3o=20para=200.7.0=20no=20setup.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 68 ++++++++++++++++++++++++------ pynfe/utils/flags.py | 3 ++ pynfe/utils/webservices.py | 36 ++++++++++++++++ setup.py | 2 +- 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 5dc1c4f8..ece235a5 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -14,15 +14,18 @@ NAMESPACE_MDFE, NAMESPACE_MDFE_METODO, NAMESPACE_METODO, + NAMESPACE_NFCOM, + NAMESPACE_NFCOM_METODO, NAMESPACE_NFE, NAMESPACE_SOAP, NAMESPACE_XSD, NAMESPACE_XSI, VERSAO_CTE, VERSAO_MDFE, + VERSAO_NFCOM, VERSAO_PADRAO, ) -from pynfe.utils.webservices import CTE, MDFE, NFCE, NFE, NFSE +from pynfe.utils.webservices import CTE, MDFE, NFCE, NFCOM, NFE, NFSE from .assinatura import AssinaturaA1 @@ -164,13 +167,21 @@ def consulta_nota(self, modelo, chave, contingencia=False): """ # url do serviço url = self._get_url(modelo=modelo, consulta="CHAVE", contingencia=contingencia) - # Monta XML do corpo da requisição - raiz = etree.Element("consSitNFe", versao=VERSAO_PADRAO, xmlns=NAMESPACE_NFE) - etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) - etree.SubElement(raiz, "xServ").text = "CONSULTAR" - etree.SubElement(raiz, "chNFe").text = chave - # Monta XML para envio da requisição - xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) + if modelo == "nfcom": + raiz = etree.Element("consSitNfcom", versao=VERSAO_NFCOM, xmlns=NAMESPACE_NFCOM) + etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) + etree.SubElement(raiz, "xServ").text = "CONSULTAR" + etree.SubElement(raiz, "chNfcom").text = chave + + xml = self._construir_xml_soap("NFComConsulta", raiz) + else: + # Monta XML do corpo da requisição + raiz = etree.Element("consSitNFe", versao=VERSAO_PADRAO, xmlns=NAMESPACE_NFE) + etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) + etree.SubElement(raiz, "xServ").text = "CONSULTAR" + etree.SubElement(raiz, "chNFe").text = chave + # Monta XML para envio da requisição + xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) return self._post(url, xml) def consulta_distribuicao( @@ -496,8 +507,14 @@ def _get_url(self, modelo, consulta, contingencia=False): else: # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE[self.uf.upper()][ambiente] + NFCE[self.uf.upper()][consulta] + elif modelo == "nfcom": + if self.uf.upper() in ["MG", "MT", "MS"]: + self.url = NFCOM[self.uf.upper()][ambiente] + NFCOM[self.uf.upper()][consulta] + else: + self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] + else: - raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') + raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce" ou "nfcom"') # Estados que utilizam outros ambientes else: lista_svrs = [ @@ -528,8 +545,12 @@ def _get_url(self, modelo, consulta, contingencia=False): elif modelo == "nfce": # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] + elif modelo == "nfcom": + self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] else: - raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') + raise Exception( + 'Modelo não encontrado! Defina modelo="nfe" ou "nfce" ou "nfcom"' + ) # unico UF que utiliza SVAN ainda para NF-e # SVRS para NFC-e elif self.uf.upper() == "MA": @@ -543,28 +564,47 @@ def _get_url(self, modelo, consulta, contingencia=False): elif modelo == "nfce": # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE["SVRS"][ambiente] + NFCE["SVRS"][consulta] + elif modelo == "nfcom": + self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] else: - raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce"') + raise Exception( + 'Modelo não encontrado! Defina modelo="nfe" ou "nfce" ou "nfcom"' + ) else: raise Exception(f"Url não encontrada para {modelo} e {consulta} {self.uf.upper()}") return self.url def _construir_xml_soap(self, metodo, dados, cabecalho=False): - """Mota o XML para o envio via SOAP""" + """Monta o XML para o envio via SOAP""" raiz = etree.Element( "{%s}Envelope" % NAMESPACE_SOAP, - nsmap={"xsi": NAMESPACE_XSI, "xsd": NAMESPACE_XSD, "soap": NAMESPACE_SOAP}, + nsmap={ + "xsi": NAMESPACE_XSI, + "xsd": NAMESPACE_XSD, + "soap": NAMESPACE_SOAP, + }, ) + body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) - # distribuição tem um corpo de xml diferente + + # === NFe Distribuição === if metodo == "NFeDistribuicaoDFe": x = etree.SubElement(body, "nfeDistDFeInteresse", xmlns=NAMESPACE_METODO + metodo) a = etree.SubElement(x, "nfeDadosMsg") + + # === Cadastro MT === elif metodo == "CadConsultaCadastro4" and self.uf.upper() == "MT": x = etree.SubElement(body, "consultaCadastro", xmlns=NAMESPACE_METODO + metodo) a = etree.SubElement(x, "nfeDadosMsg") + + # === NFCOM Consulta === + elif metodo == "NFComConsulta": + a = etree.SubElement(body, "nfcomDadosMsg", xmlns=NAMESPACE_NFCOM_METODO + metodo) + + # === Default (NFe / NFCe / CTe etc) === else: a = etree.SubElement(body, "nfeDadosMsg", xmlns=NAMESPACE_METODO + metodo) + a.append(dados) return raiz diff --git a/pynfe/utils/flags.py b/pynfe/utils/flags.py index 589eab61..35c87368 100644 --- a/pynfe/utils/flags.py +++ b/pynfe/utils/flags.py @@ -21,6 +21,9 @@ NAMESPACE_CTE_METODO = "http://www.portalfiscal.inf.br/cte/wsdl/" VERSAO_CTE = "3.00" +NAMESPACE_NFCOM = "http://www.portalfiscal.inf.br/nfcom" +NAMESPACE_NFCOM_METODO = "http://www.portalfiscal.inf.br/nfcom/wsdl/" +VERSAO_NFCOM = "1.00" VERSAO_QRCODE = "2" TIPOS_DOCUMENTO = ( diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 358ba425..b3bccee8 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -608,3 +608,39 @@ "HOMOLOGACAO": "https://homologacao.nfe.", }, } + +# NFCOM +NFCOM = { + "MG": { + "AUTORIZACAO": "fazenda.mg.gov.br/nfcom/services/NFComRecepcao?wsdl", + "CHAVE": "fazenda.mg.gov.br/nfcom/services/NFComConsulta?wsdl", + "EVENTOS": "fazenda.mg.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", + "STATUS": "fazenda.mg.gov.br/nfcom/services/NFComStatusServico?wsdl", + "HTTPS": "https://nfcom.", + "HOMOLOGACAO": "https://hnfcom.", + }, + "MS": { + "AUTORIZACAO": "sefaz.ms.gov.br/ws/NFComRecepcao?wsdl", + "CHAVE": "sefaz.ms.gov.br/ws/NFComConsulta?wsdl", + "EVENTOS": "sefaz.ms.gov.br/ws/NFComRecepcaoEvento?wsdl", + "STATUS": "sefaz.ms.gov.br/ws/NFComStatusServico?wsdl", + "HTTPS": "https://nfcom.", + "HOMOLOGACAO": "https://hom.nfcom.", + }, + "MT": { + "AUTORIZACAO": "sefaz.mt.gov.br/nfcom/services/NFComRecepcao?wsdl", + "CHAVE": "sefaz.mt.gov.br/nfcom/services/NFComConsulta?wsdl", + "EVENTOS": "sefaz.mt.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", + "STATUS": "sefaz.mt.gov.br/nfcom/services/NFComStatusServico?wsdl", + "HTTPS": "https://www.", + "HOMOLOGACAO": "https://homologacao.", + }, + "SVRS": { + "AUTORIZACAO": "svrs.rs.gov.br/WS/NFComConsulta/NFComConsulta.asmx", + "CHAVE": "svrs.rs.gov.br/WS/NFComRecepcao/NFComRecepcao.asmx", + "EVENTOS": "svrs.rs.gov.br/WS/NFComRecepcaoEvento/NFComRecepcaoEvento.asmx", + "STATUS": "svrs.rs.gov.br/WS/NFComStatusServico/NFComStatusServico.asmx", + "HTTPS": "https://nfcom.", + "HOMOLOGACAO": "https://nfcom-homologacao.", + } +} \ No newline at end of file diff --git a/setup.py b/setup.py index 4a356cd4..8c5a63a6 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.6.2", + version="0.7.0", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 8b269910434655ba33b2e6874a7ada7de5ea1074 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 11:39:49 -0300 Subject: [PATCH 061/175] =?UTF-8?q?feat:=20adiciona=20suporte=20ao=20SOAPA?= =?UTF-8?q?ction=20na=20classe=20ComunicacaoSefaz=20para=20requisi=C3=A7?= =?UTF-8?q?=C3=B5es=20NFCom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 13 +++++++++---- setup.py | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ece235a5..84605d5f 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -167,6 +167,7 @@ def consulta_nota(self, modelo, chave, contingencia=False): """ # url do serviço url = self._get_url(modelo=modelo, consulta="CHAVE", contingencia=contingencia) + soap_action = None if modelo == "nfcom": raiz = etree.Element("consSitNfcom", versao=VERSAO_NFCOM, xmlns=NAMESPACE_NFCOM) etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) @@ -174,6 +175,7 @@ def consulta_nota(self, modelo, chave, contingencia=False): etree.SubElement(raiz, "chNfcom").text = chave xml = self._construir_xml_soap("NFComConsulta", raiz) + soap_action = "nfcomConsultaNF" else: # Monta XML do corpo da requisição raiz = etree.Element("consSitNFe", versao=VERSAO_PADRAO, xmlns=NAMESPACE_NFE) @@ -182,7 +184,7 @@ def consulta_nota(self, modelo, chave, contingencia=False): etree.SubElement(raiz, "chNFe").text = chave # Monta XML para envio da requisição xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) - return self._post(url, xml) + return self._post(url, xml, soap_action=soap_action) def consulta_distribuicao( self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False @@ -608,7 +610,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): a.append(dados) return raiz - def _post_header(self): + def _post_header(self, soap_action=None): """Retorna um dicionário com os atributos para o cabeçalho da requisição HTTP""" # PE é a única UF que exige SOAPAction no header response = { @@ -617,9 +619,12 @@ def _post_header(self): } if self.uf.upper() == "PE": response["SOAPAction"] = "" + + if soap_action: + response["SOAPAction"] = soap_action return response - def _post(self, url, xml, timeout=None): + def _post(self, url, xml, timeout=None, soap_action=None): certificado_a1 = CertificadoA1(self.certificado) chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) chave_cert = (cert, chave) @@ -638,7 +643,7 @@ def _post(self, url, xml, timeout=None): result = requests.post( url, xml, - headers=self._post_header(), + headers=self._post_header(soap_action=soap_action), cert=chave_cert, verify=False, timeout=timeout, diff --git a/setup.py b/setup.py index 8c5a63a6..32b57bce 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="PyNFe", - version="0.7.0", + version="0.7.1", author="TadaSoftware", author_email="tadasoftware@gmail.com", description="Interface library with the Brazilian Electronic Invoice web services", From 0c684f65e60e54d57f4560b8fe881dd30fde2bdc Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 11:42:17 -0300 Subject: [PATCH 062/175] =?UTF-8?q?feat:=20adiciona=20logs=20de=20depura?= =?UTF-8?q?=C3=A7=C3=A3o=20na=20classe=20ComunicacaoSefaz=20para=20facilit?= =?UTF-8?q?ar=20o=20diagn=C3=B3stico=20de=20requisi=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 84605d5f..32cb5d16 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -640,6 +640,9 @@ def _post(self, url, xml, timeout=None, soap_action=None): ) xml = xml_declaration + xml # Faz o request com o servidor + print(xml) + print(url) + print(self._post_header(soap_action=soap_action)) result = requests.post( url, xml, From 43c5dcd92c6a8257724efea06dfb280ead3092d9 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 11:45:55 -0300 Subject: [PATCH 063/175] =?UTF-8?q?feat:=20atualiza=20o=20cabe=C3=A7alho?= =?UTF-8?q?=20da=20resposta=20SOAP=20para=20incluir=20o=20content-type=20c?= =?UTF-8?q?om=20a=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 32cb5d16..b5b94915 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -621,7 +621,7 @@ def _post_header(self, soap_action=None): response["SOAPAction"] = "" if soap_action: - response["SOAPAction"] = soap_action + response["content-type"] = f"application/soap+xml; charset=utf-8; action={soap_action}" return response def _post(self, url, xml, timeout=None, soap_action=None): From 29f50e77951443f8bdbb92872ea99dd84e284e9e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 12:03:42 -0300 Subject: [PATCH 064/175] feat: corrige elementos XML e remove uso de SOAPAction na classe ComunicacaoSefaz --- pynfe/processamento/comunicacao.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index b5b94915..49621a6a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -167,15 +167,13 @@ def consulta_nota(self, modelo, chave, contingencia=False): """ # url do serviço url = self._get_url(modelo=modelo, consulta="CHAVE", contingencia=contingencia) - soap_action = None if modelo == "nfcom": - raiz = etree.Element("consSitNfcom", versao=VERSAO_NFCOM, xmlns=NAMESPACE_NFCOM) + raiz = etree.Element("consSitNFCom", versao=VERSAO_NFCOM, xmlns=NAMESPACE_NFCOM) etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) etree.SubElement(raiz, "xServ").text = "CONSULTAR" - etree.SubElement(raiz, "chNfcom").text = chave + etree.SubElement(raiz, "chNFCom").text = chave xml = self._construir_xml_soap("NFComConsulta", raiz) - soap_action = "nfcomConsultaNF" else: # Monta XML do corpo da requisição raiz = etree.Element("consSitNFe", versao=VERSAO_PADRAO, xmlns=NAMESPACE_NFE) @@ -184,7 +182,7 @@ def consulta_nota(self, modelo, chave, contingencia=False): etree.SubElement(raiz, "chNFe").text = chave # Monta XML para envio da requisição xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) - return self._post(url, xml, soap_action=soap_action) + return self._post(url, xml) def consulta_distribuicao( self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False @@ -610,7 +608,7 @@ def _construir_xml_soap(self, metodo, dados, cabecalho=False): a.append(dados) return raiz - def _post_header(self, soap_action=None): + def _post_header(self): """Retorna um dicionário com os atributos para o cabeçalho da requisição HTTP""" # PE é a única UF que exige SOAPAction no header response = { @@ -620,8 +618,6 @@ def _post_header(self, soap_action=None): if self.uf.upper() == "PE": response["SOAPAction"] = "" - if soap_action: - response["content-type"] = f"application/soap+xml; charset=utf-8; action={soap_action}" return response def _post(self, url, xml, timeout=None, soap_action=None): @@ -642,11 +638,10 @@ def _post(self, url, xml, timeout=None, soap_action=None): # Faz o request com o servidor print(xml) print(url) - print(self._post_header(soap_action=soap_action)) result = requests.post( url, xml, - headers=self._post_header(soap_action=soap_action), + headers=self._post_header(), cert=chave_cert, verify=False, timeout=timeout, From 8ed27ede56fb68b4b6215e2d0e102cc670ca398f Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 13:01:04 -0300 Subject: [PATCH 065/175] =?UTF-8?q?feat:=20adiciona=20m=C3=A9todo=20para?= =?UTF-8?q?=20download=20de=20NF-e/NFC-e=20e=20atualiza=20URLs=20de=20down?= =?UTF-8?q?load=20para=20NFCOM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 64 ++++++++++++++++++++++++++++-- pynfe/utils/webservices.py | 5 ++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 49621a6a..05ad98c1 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import datetime import re - +import html import requests from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils import etree, so_numeros @@ -172,7 +172,6 @@ def consulta_nota(self, modelo, chave, contingencia=False): etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) etree.SubElement(raiz, "xServ").text = "CONSULTAR" etree.SubElement(raiz, "chNFCom").text = chave - xml = self._construir_xml_soap("NFComConsulta", raiz) else: # Monta XML do corpo da requisição @@ -183,6 +182,65 @@ def consulta_nota(self, modelo, chave, contingencia=False): # Monta XML para envio da requisição xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) return self._post(url, xml) + + def download_nota(self, modelo, chave, contingencia=False): + """ + Este método oferece o download da NF-e/NFC-e na Base de Dados do Portal + da Secretaria de Fazenda Estadual. + :param modelo: Modelo da nota + :param chave: Chave da nota + :param contingencia: Indica se o envio é em contingência ou não + :return: + """ + # url do serviço + url = self._get_url(modelo=modelo, consulta="DOWNLOAD", contingencia=contingencia) + + if modelo == "nfcom": + certificado_a1 = CertificadoA1(self.certificado) + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) + chave_cert = (cert, chave) + # Abre a conexão HTTPS + try: + response = requests.post( + url, + data={ + "sistema": "Nfcom", + "OrigemSite": "0", + "Ambiente": self._ambiente, # 1=Produção, 2=Homologação + "ChaveAcessoDfe": chave + }, + headers={ + "User-Agent": "Mozilla/5.0", + "Content-Type": "application/x-www-form-urlencoded", + "Referer": "https://dfe-portal.svrs.rs.gov.br/Nfcom", + }, + cert=chave_cert, + verify=False, + timeout=30, + ) + + pattern = r'"xml"\s*:\s*"(.+?)"\s*\}' + match = re.search(pattern, response.text, re.DOTALL) + + if not match: + raise ValueError("XML não encontrado no HTML") + + xml_escaped = match.group(1) + + # Remove escapes JavaScript + xml = xml_escaped.encode("utf-8").decode("unicode_escape") + + # Converte entidades HTML (& etc) + xml = html.unescape(xml) + return xml.strip() + except requests.exceptions.RequestException as e: + raise e + finally: + certificado_a1.excluir() + + else: + raise NotImplementedError("Download not implemented for this model yet.") + def consulta_distribuicao( self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False @@ -620,7 +678,7 @@ def _post_header(self): return response - def _post(self, url, xml, timeout=None, soap_action=None): + def _post(self, url, xml, timeout=None): certificado_a1 = CertificadoA1(self.certificado) chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) chave_cert = (cert, chave) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index b3bccee8..8fb82640 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -609,9 +609,9 @@ }, } -# NFCOM NFCOM = { "MG": { + "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "fazenda.mg.gov.br/nfcom/services/NFComRecepcao?wsdl", "CHAVE": "fazenda.mg.gov.br/nfcom/services/NFComConsulta?wsdl", "EVENTOS": "fazenda.mg.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", @@ -620,6 +620,7 @@ "HOMOLOGACAO": "https://hnfcom.", }, "MS": { + "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "sefaz.ms.gov.br/ws/NFComRecepcao?wsdl", "CHAVE": "sefaz.ms.gov.br/ws/NFComConsulta?wsdl", "EVENTOS": "sefaz.ms.gov.br/ws/NFComRecepcaoEvento?wsdl", @@ -628,6 +629,7 @@ "HOMOLOGACAO": "https://hom.nfcom.", }, "MT": { + "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "sefaz.mt.gov.br/nfcom/services/NFComRecepcao?wsdl", "CHAVE": "sefaz.mt.gov.br/nfcom/services/NFComConsulta?wsdl", "EVENTOS": "sefaz.mt.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", @@ -636,6 +638,7 @@ "HOMOLOGACAO": "https://homologacao.", }, "SVRS": { + "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "svrs.rs.gov.br/WS/NFComConsulta/NFComConsulta.asmx", "CHAVE": "svrs.rs.gov.br/WS/NFComRecepcao/NFComRecepcao.asmx", "EVENTOS": "svrs.rs.gov.br/WS/NFComRecepcaoEvento/NFComRecepcaoEvento.asmx", From b43aa7d56abaa515865f0f0a8c0547de629ca4f4 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 13:04:30 -0300 Subject: [PATCH 066/175] =?UTF-8?q?feat:=20atualiza=20URLs=20de=20download?= =?UTF-8?q?=20para=20NFCOM=20e=20ajusta=20l=C3=B3gica=20de=20consulta=20na?= =?UTF-8?q?=20classe=20ComunicacaoSefaz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 9 ++++++--- pynfe/utils/webservices.py | 3 --- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 05ad98c1..63bae5cf 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -566,10 +566,13 @@ def _get_url(self, modelo, consulta, contingencia=False): # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 self.url = NFCE[self.uf.upper()][ambiente] + NFCE[self.uf.upper()][consulta] elif modelo == "nfcom": - if self.uf.upper() in ["MG", "MT", "MS"]: - self.url = NFCOM[self.uf.upper()][ambiente] + NFCOM[self.uf.upper()][consulta] + if consulta == "DOWNLOAD": + self.url = NFCOM["SVRS"][consulta] else: - self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] + if self.uf.upper() in ["MG", "MT", "MS"]: + self.url = NFCOM[self.uf.upper()][ambiente] + NFCOM[self.uf.upper()][consulta] + else: + self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] else: raise Exception('Modelo não encontrado! Defina modelo="nfe" ou "nfce" ou "nfcom"') diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 8fb82640..68256b22 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -611,7 +611,6 @@ NFCOM = { "MG": { - "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "fazenda.mg.gov.br/nfcom/services/NFComRecepcao?wsdl", "CHAVE": "fazenda.mg.gov.br/nfcom/services/NFComConsulta?wsdl", "EVENTOS": "fazenda.mg.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", @@ -620,7 +619,6 @@ "HOMOLOGACAO": "https://hnfcom.", }, "MS": { - "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "sefaz.ms.gov.br/ws/NFComRecepcao?wsdl", "CHAVE": "sefaz.ms.gov.br/ws/NFComConsulta?wsdl", "EVENTOS": "sefaz.ms.gov.br/ws/NFComRecepcaoEvento?wsdl", @@ -629,7 +627,6 @@ "HOMOLOGACAO": "https://hom.nfcom.", }, "MT": { - "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", "AUTORIZACAO": "sefaz.mt.gov.br/nfcom/services/NFComRecepcao?wsdl", "CHAVE": "sefaz.mt.gov.br/nfcom/services/NFComConsulta?wsdl", "EVENTOS": "sefaz.mt.gov.br/nfcom/services/NFComRecepcaoEvento?wsdl", From ded80996739114d5ad10cc76d7f5d1f0f1c03c0e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 13:06:16 -0300 Subject: [PATCH 067/175] =?UTF-8?q?feat:=20adiciona=20logs=20de=20depura?= =?UTF-8?q?=C3=A7=C3=A3o=20na=20classe=20ComunicacaoSefaz=20para=20facilit?= =?UTF-8?q?ar=20o=20diagn=C3=B3stico=20de=20erros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 63bae5cf..fcbb771d 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -223,6 +223,7 @@ def download_nota(self, modelo, chave, contingencia=False): match = re.search(pattern, response.text, re.DOTALL) if not match: + print(response.text) # DEBUG raise ValueError("XML não encontrado no HTML") xml_escaped = match.group(1) @@ -697,8 +698,6 @@ def _post(self, url, xml, timeout=None): ) xml = xml_declaration + xml # Faz o request com o servidor - print(xml) - print(url) result = requests.post( url, xml, From 89c3955399e0788bd5fd4d3875ef7770a69f79ba Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 13:16:13 -0300 Subject: [PATCH 068/175] =?UTF-8?q?feat:=20implementa=20l=C3=B3gica=20de?= =?UTF-8?q?=20retry=20na=20consulta=20de=20download=20na=20classe=20Comuni?= =?UTF-8?q?cacaoSefaz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 47 +++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index fcbb771d..751b1d9b 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import datetime +import time import re import html import requests @@ -201,26 +202,32 @@ def download_nota(self, modelo, chave, contingencia=False): chave_cert = (cert, chave) # Abre a conexão HTTPS try: - response = requests.post( - url, - data={ - "sistema": "Nfcom", - "OrigemSite": "0", - "Ambiente": self._ambiente, # 1=Produção, 2=Homologação - "ChaveAcessoDfe": chave - }, - headers={ - "User-Agent": "Mozilla/5.0", - "Content-Type": "application/x-www-form-urlencoded", - "Referer": "https://dfe-portal.svrs.rs.gov.br/Nfcom", - }, - cert=chave_cert, - verify=False, - timeout=30, - ) - - pattern = r'"xml"\s*:\s*"(.+?)"\s*\}' - match = re.search(pattern, response.text, re.DOTALL) + session = requests.Session() + for _ in range(3): + response = session.post( + url, + data={ + "sistema": "Nfcom", + "OrigemSite": "0", + "Ambiente": self._ambiente, # 1=Produção, 2=Homologação + "ChaveAcessoDfe": chave + }, + headers={ + "User-Agent": "Mozilla/5.0", + "Content-Type": "application/x-www-form-urlencoded", + "Referer": "https://dfe-portal.svrs.rs.gov.br/Nfcom", + }, + cert=chave_cert, + verify=False, + timeout=30, + ) + + pattern = r'"xml"\s*:\s*"(.+?)"\s*\}' + match = re.search(pattern, response.text, re.DOTALL) + if not match: + print(response.text) # DEBUG + print("Retrying download...") # DEBUG + time.sleep(2) # Wait before retrying if not match: print(response.text) # DEBUG From 794fa82d05674dc50d4412c746387c1a2ebcaa2e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 13:41:26 -0300 Subject: [PATCH 069/175] =?UTF-8?q?feat:=20implementa=20download=20da=20NF?= =?UTF-8?q?Com=20via=20Portal=20SVRS=20com=20valida=C3=A7=C3=A3o=20de=20ch?= =?UTF-8?q?ave=20e=20tratamento=20de=20bloqueio=20por=20IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 131 ++++++++++++++++------------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 751b1d9b..346ee080 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -186,69 +186,82 @@ def consulta_nota(self, modelo, chave, contingencia=False): def download_nota(self, modelo, chave, contingencia=False): """ - Este método oferece o download da NF-e/NFC-e na Base de Dados do Portal - da Secretaria de Fazenda Estadual. - :param modelo: Modelo da nota - :param chave: Chave da nota - :param contingencia: Indica se o envio é em contingência ou não - :return: + Download da NFCom via Portal SVRS (HTML + extração do XML via JS). """ - # url do serviço + url = self._get_url(modelo=modelo, consulta="DOWNLOAD", contingencia=contingencia) - - if modelo == "nfcom": - certificado_a1 = CertificadoA1(self.certificado) - chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) - chave_cert = (cert, chave) - # Abre a conexão HTTPS - try: - session = requests.Session() - for _ in range(3): - response = session.post( - url, - data={ - "sistema": "Nfcom", - "OrigemSite": "0", - "Ambiente": self._ambiente, # 1=Produção, 2=Homologação - "ChaveAcessoDfe": chave - }, - headers={ - "User-Agent": "Mozilla/5.0", - "Content-Type": "application/x-www-form-urlencoded", - "Referer": "https://dfe-portal.svrs.rs.gov.br/Nfcom", - }, - cert=chave_cert, - verify=False, - timeout=30, - ) - - pattern = r'"xml"\s*:\s*"(.+?)"\s*\}' - match = re.search(pattern, response.text, re.DOTALL) - if not match: - print(response.text) # DEBUG - print("Retrying download...") # DEBUG - time.sleep(2) # Wait before retrying - - if not match: - print(response.text) # DEBUG - raise ValueError("XML não encontrado no HTML") - - xml_escaped = match.group(1) - - # Remove escapes JavaScript - xml = xml_escaped.encode("utf-8").decode("unicode_escape") - - # Converte entidades HTML (& etc) - xml = html.unescape(xml) - return xml.strip() - except requests.exceptions.RequestException as e: - raise e - finally: - certificado_a1.excluir() - - else: + + if modelo != "nfcom": raise NotImplementedError("Download not implemented for this model yet.") + # validação da chave + if not chave or not chave.isdigit() or len(chave) != 44: + raise ValueError("Chave NFCom inválida (esperado 44 dígitos numéricos)") + + certificado_a1 = CertificadoA1(self.certificado) + + try: + key_path, cert_path = certificado_a1.separar_arquivo( + self.certificado_senha, + caminho=True + ) + cert = (cert_path, key_path) + + session = requests.Session() + + response = session.post( + url, + files={ + "sistema": (None, "Nfcom"), + "OrigemSite": (None, "0"), + "Ambiente": (None, str(self._ambiente)), + "ChaveAcessoDfe": (None, chave), + }, + headers={ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", + "Referer": "https://dfe-portal.svrs.rs.gov.br/Nfcom", + }, + cert=cert, + verify=False, + timeout=30, + ) + + html_body = response.text + + # 🚫 BLOQUEIO POR IP (rate limit) + if ( + 'textoErro' in html_body and + 'IP não autorizado' in html_body + ): + raise PermissionError( + "SVRS bloqueou o IP por múltiplas consultas simultâneas. " + "Aguarde liberação ou utilize outro IP." + ) + + # 🔍 extrai o XML do JS + pattern = r'var\s+stringJson\s*=\s*\{\s*"xml"\s*:\s*"(.+?)"\s*\};' + match = re.search(pattern, html_body, re.DOTALL) + + if not match: + raise ValueError( + "XML não encontrado no HTML retornado pelo portal SVRS. " + "Pode ser chave inexistente, ambiente incorreto ou NFCom sem permissão." + ) + + xml_js = match.group(1) + + # remove escapes JavaScript + xml = bytes(xml_js, "utf-8").decode("unicode_escape") + + # converte entidades HTML (& etc) + xml = html.unescape(xml) + + return xml.strip() + + finally: + certificado_a1.excluir() + + def consulta_distribuicao( self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False From 759334a42e4c260f38ff5fd0a6e4574572a1a94f Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 14:00:50 -0300 Subject: [PATCH 070/175] =?UTF-8?q?feat:=20refatora=20m=C3=A9todo=20de=20d?= =?UTF-8?q?ownload=20da=20NFCom=20para=20melhorar=20a=20legibilidade=20e?= =?UTF-8?q?=20tratamento=20de=20erros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 346ee080..61d5293c 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- import datetime -import time -import re import html +import re + import requests from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils import etree, so_numeros @@ -183,7 +183,7 @@ def consulta_nota(self, modelo, chave, contingencia=False): # Monta XML para envio da requisição xml = self._construir_xml_soap("NFeConsultaProtocolo4", raiz) return self._post(url, xml) - + def download_nota(self, modelo, chave, contingencia=False): """ Download da NFCom via Portal SVRS (HTML + extração do XML via JS). @@ -202,8 +202,7 @@ def download_nota(self, modelo, chave, contingencia=False): try: key_path, cert_path = certificado_a1.separar_arquivo( - self.certificado_senha, - caminho=True + self.certificado_senha, caminho=True ) cert = (cert_path, key_path) @@ -229,10 +228,7 @@ def download_nota(self, modelo, chave, contingencia=False): html_body = response.text # 🚫 BLOQUEIO POR IP (rate limit) - if ( - 'textoErro' in html_body and - 'IP não autorizado' in html_body - ): + if "textoErro" in html_body and "IP não autorizado" in html_body: raise PermissionError( "SVRS bloqueou o IP por múltiplas consultas simultâneas. " "Aguarde liberação ou utilize outro IP." @@ -261,8 +257,6 @@ def download_nota(self, modelo, chave, contingencia=False): finally: certificado_a1.excluir() - - def consulta_distribuicao( self, cnpj=None, cpf=None, chave=None, nsu=0, consulta_nsu_especifico=False ): @@ -591,7 +585,9 @@ def _get_url(self, modelo, consulta, contingencia=False): self.url = NFCOM["SVRS"][consulta] else: if self.uf.upper() in ["MG", "MT", "MS"]: - self.url = NFCOM[self.uf.upper()][ambiente] + NFCOM[self.uf.upper()][consulta] + self.url = ( + NFCOM[self.uf.upper()][ambiente] + NFCOM[self.uf.upper()][consulta] + ) else: self.url = NFCOM["SVRS"][ambiente] + NFCOM["SVRS"][consulta] @@ -717,6 +713,8 @@ def _post(self, url, xml, timeout=None): etree.tostring(xml, encoding="unicode").replace("\n", ""), ) xml = xml_declaration + xml + print(xml) + print("URL:", url) # Faz o request com o servidor result = requests.post( url, From 66cc0ae80baca42db11542bb8718f043b226d5ca Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 24 Dec 2025 14:10:41 -0300 Subject: [PATCH 071/175] =?UTF-8?q?refatora=20URLs=20de=20servi=C3=A7os=20?= =?UTF-8?q?NFE=20e=20NFCOM=20para=20melhorar=20a=20legibilidade=20e=20cons?= =?UTF-8?q?ist=C3=AAncia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/utils/webservices.py | 80 ++++++++++++-------------------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 68256b22..66cb32c9 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -1,5 +1,5 @@ """ - @author: Junior Tada, Leonardo Tada +@author: Junior Tada, Leonardo Tada """ # http://nfce.encat.org/desenvolvedor/qrcode/ @@ -299,9 +299,7 @@ NFE = { # Alguns serviços são disponibilizados apenas pelo ambiente nacional "AN": { - "EVENTOS": ( - "nfe.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx" - ), # versao: 4.00 + "EVENTOS": "nfe.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx", # versao: 4.00 "DISTRIBUICAO": "nfe.fazenda.gov.br/NFeDistribuicaoDFe/NFeDistribuicaoDFe.asmx", "HTTPS": "https://www", "HOMOLOGACAO": "https://hom1", @@ -317,11 +315,7 @@ "HTTPS": "https://", "HOMOLOGACAO": "https://hom", }, - "MA": { - "CADASTRO": ( - "https://sistemas.sefaz.ma.gov.br/wscadastro/CadConsultaCadastro2?wsdl" - ) - }, + "MA": {"CADASTRO": "https://sistemas.sefaz.ma.gov.br/wscadastro/CadConsultaCadastro2?wsdl"}, "PE": { "STATUS": "sefaz.pe.gov.br/nfe-service/services/NFeStatusServico4", "AUTORIZACAO": "sefaz.pe.gov.br/nfe-service/services/NFeAutorizacao4", @@ -334,24 +328,12 @@ "HOMOLOGACAO": "https://nfehomolog.", }, "BA": { - "STATUS": ( - "nfe.sefaz.ba.gov.br/webservices/NFeStatusServico4/NFeStatusServico4.asmx" - ), - "AUTORIZACAO": ( - "nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx" - ), - "RECIBO": ( - "nfe.sefaz.ba.gov.br/webservices/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx" - ), - "CHAVE": ( - "nfe.sefaz.ba.gov.br/webservices/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx" - ), - "INUTILIZACAO": ( - "nfe.sefaz.ba.gov.br/webservices/NFeInutilizacao4/NFeInutilizacao4.asmx" - ), - "EVENTOS": ( - "nfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx" - ), + "STATUS": "nfe.sefaz.ba.gov.br/webservices/NFeStatusServico4/NFeStatusServico4.asmx", + "AUTORIZACAO": "nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx", + "RECIBO": "nfe.sefaz.ba.gov.br/webservices/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx", + "CHAVE": "nfe.sefaz.ba.gov.br/webservices/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx", + "INUTILIZACAO": "nfe.sefaz.ba.gov.br/webservices/NFeInutilizacao4/NFeInutilizacao4.asmx", + "EVENTOS": "nfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx", "CADASTRO": ( "nfe.sefaz.ba.gov.br/webservices/CadConsultaCadastro4/CadConsultaCadastro4.asmx" ), @@ -381,14 +363,10 @@ "HOMOLOGACAO": "https://homologacao.", }, "PR": { - "STATUS": ( - "nfe.sefa.pr.gov.br/nfe/NFeStatusServico4" - ), # CONSULTA STATUS DO SERVICO + "STATUS": "nfe.sefa.pr.gov.br/nfe/NFeStatusServico4", # CONSULTA STATUS DO SERVICO "AUTORIZACAO": "nfe.sefa.pr.gov.br/nfe/NFeAutorizacao4", # AUTORIZACAO "RECIBO": "nfe.sefa.pr.gov.br/nfe/NFeRetAutorizacao4", # CONSULTA RECIBO - "CHAVE": ( - "nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4" - ), # CONSULTA CHAVE DE ACESSO + "CHAVE": "nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4", # CONSULTA CHAVE DE ACESSO "INUTILIZACAO": "nfe.sefa.pr.gov.br/nfe/NFeInutilizacao4", # INUTILIZAÇAO "EVENTOS": "nfe.sefa.pr.gov.br/nfe/NFeRecepcaoEvento4", # REGISTRO DE EVENTOS "CADASTRO": "nfe.sefa.pr.gov.br/nfe/CadConsultaCadastro4", # CONSULTA CADASTRO @@ -445,22 +423,12 @@ "HOMOLOGACAO": "https://homolog.", }, "SVAN": { - "STATUS": ( - "sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx" - ), - "AUTORIZACAO": ( - "sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx" - ), - "RECIBO": ( - "sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx" - ), + "STATUS": "sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx", + "AUTORIZACAO": "sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx", + "RECIBO": "sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx", "CHAVE": "sefazvirtual.fazenda.gov.br/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx", - "INUTILIZACAO": ( - "sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx" - ), - "EVENTOS": ( - "sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx" - ), + "INUTILIZACAO": "sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx", + "EVENTOS": "sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx", "DOWNLOAD": "sefazvirtual.fazenda.gov.br/NfeDownloadNF/NfeDownloadNF.asmx", "HTTPS": "https://www.", "HOMOLOGACAO": "https://hom.", @@ -509,9 +477,7 @@ "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", "SUBSTITUIR": "SubstituirNfse", "HTTPS": "http://e-gov.betha.com.br/e-nota-contribuinte-ws/nfseWS?wsdl", - "HOMOLOGACAO": ( - "http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl" - ), + "HOMOLOGACAO": "http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl", }, # "GINFES": { @@ -532,7 +498,9 @@ "CANCELAR_LOTE": "CancelarNotaLote", "CONSULTA": "Consultar", "HTTPS": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", - "HOMOLOGACAO": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", + "HOMOLOGACAO": ( + "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl" + ), }, } @@ -636,11 +604,11 @@ }, "SVRS": { "DOWNLOAD": "https://dfe-portal.svrs.rs.gov.br/NfcomSSL/DownloadXmlDfe", - "AUTORIZACAO": "svrs.rs.gov.br/WS/NFComConsulta/NFComConsulta.asmx", - "CHAVE": "svrs.rs.gov.br/WS/NFComRecepcao/NFComRecepcao.asmx", + "AUTORIZACAO": "svrs.rs.gov.br/WS/NFComRecepcao/NFComRecepcao.asmx", + "CHAVE": "svrs.rs.gov.br/WS/NFComConsulta/NFComConsulta.asmx", "EVENTOS": "svrs.rs.gov.br/WS/NFComRecepcaoEvento/NFComRecepcaoEvento.asmx", "STATUS": "svrs.rs.gov.br/WS/NFComStatusServico/NFComStatusServico.asmx", "HTTPS": "https://nfcom.", "HOMOLOGACAO": "https://nfcom-homologacao.", - } -} \ No newline at end of file + }, +} From f905fb062300d30d71e30e796238922b740a8aa8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:00:40 -0300 Subject: [PATCH 072/175] =?UTF-8?q?corrige=20verifica=C3=A7=C3=A3o=20de=20?= =?UTF-8?q?refer=C3=AAncia=20na=20assinatura=20XML=20para=20evitar=20erros?= =?UTF-8?q?=20de=20atributo=20None?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 50c5e263..b057078d 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -29,7 +29,7 @@ def __init__(self, certificado, senha): def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.find(".//*[@Id]").attrib["Id"] + reference = xml.find(".//*[@Id]").attrib["Id"] if xml.find(".//*[@Id]") is not None else None # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) From c81d59030556041237e5b425ca6604b354cf9a12 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:06:23 -0300 Subject: [PATCH 073/175] =?UTF-8?q?altera=20m=C3=A9todos=20de=20comunica?= =?UTF-8?q?=C3=A7=C3=A3o=20da=20NFS-e=20para=20vers=C3=A3o=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index aca316a6..446b1190 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -959,28 +959,24 @@ def _post_https(self, url, xml, metodo): if metodo == "gerar": return cliente.service.GerarNfse(cabecalho, xml) elif metodo == "enviar_lote": - return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) + return cliente.service.RecepcionarLoteRps(cabecalho, xml) elif metodo == "consulta": - return cliente.service.ConsultarNfseV3(cabecalho, xml) + return cliente.service.ConsultarNfse(cabecalho, xml) elif metodo == "consulta_lote": - return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) + return cliente.service.ConsultarLoteRps(cabecalho, xml) elif metodo == "consulta_situacao_lote": - return cliente.service.ConsultarSituacaoLoteRpsV3(cabecalho, xml) + return cliente.service.ConsultarSituacaoLoteRps(cabecalho, xml) elif metodo == "consultaRps": - return cliente.service.ConsultarNfsePorRpsV3(cabecalho, xml) + return cliente.service.ConsultarNfsePorRps(cabecalho, xml) elif metodo == "consultaFaixa": return cliente.service.ConsultarNfseFaixa(cabecalho, xml) elif metodo == "cancelar": - # versão 2 return cliente.service.CancelarNfse(xml) - # versão 3 - # return cliente.service.CancelarNfseV3(cabecalho, xml) # TODO outros metodos else: raise Exception("Método não implementado no autorizador.") except Exception as e: raise e - def enviar_barueri(self, xml, operation): url = self._get_url() From 26923a62707e409f163a09d7c04e831fee7ffef3 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:12:30 -0300 Subject: [PATCH 074/175] =?UTF-8?q?atualiza=20m=C3=A9todos=20de=20comunica?= =?UTF-8?q?=C3=A7=C3=A3o=20da=20NFS-e=20para=20vers=C3=A3o=203=20e=20remov?= =?UTF-8?q?e=20m=C3=A9todos=20obsoletos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 25 +++++++++---------------- pynfe/utils/webservices.py | 9 ++------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 446b1190..75b9708f 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -959,24 +959,28 @@ def _post_https(self, url, xml, metodo): if metodo == "gerar": return cliente.service.GerarNfse(cabecalho, xml) elif metodo == "enviar_lote": - return cliente.service.RecepcionarLoteRps(cabecalho, xml) + return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) elif metodo == "consulta": - return cliente.service.ConsultarNfse(cabecalho, xml) + return cliente.service.ConsultarNfseV3(cabecalho, xml) elif metodo == "consulta_lote": - return cliente.service.ConsultarLoteRps(cabecalho, xml) + return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) elif metodo == "consulta_situacao_lote": - return cliente.service.ConsultarSituacaoLoteRps(cabecalho, xml) + return cliente.service.ConsultarSituacaoLoteRpsV3(cabecalho, xml) elif metodo == "consultaRps": - return cliente.service.ConsultarNfsePorRps(cabecalho, xml) + return cliente.service.ConsultarNfsePorRpsV3(cabecalho, xml) elif metodo == "consultaFaixa": return cliente.service.ConsultarNfseFaixa(cabecalho, xml) elif metodo == "cancelar": + # versão 2 return cliente.service.CancelarNfse(xml) + # versão 3 + # return cliente.service.CancelarNfseV3(cabecalho, xml) # TODO outros metodos else: raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + def enviar_barueri(self, xml, operation): url = self._get_url() @@ -1520,17 +1524,6 @@ def consulta_distribuicao( xml = self._construir_xml_soap("CTeDistribuicaoDFe", raiz) return self._post(url, xml) - def consulta(self, chave): - url = self._get_url("CONSULTA") - # Monta XML do corpo da requisição - raiz = etree.Element("consSitCTe", versao=self._versao, xmlns=NAMESPACE_CTE) - etree.SubElement(raiz, "tpAmb").text = str(self._ambiente) - etree.SubElement(raiz, "xServ").text = "CONSULTAR" - etree.SubElement(raiz, "chCTe").text = chave - # Monta XML para envio da requisição - xml = self._construir_xml_soap("cteConsultaCT", raiz) - return self._post(url, xml) - def _get_url_an(self, consulta): ambiente = "https://www1." # produção if self._ambiente == 2: # homologacao diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 6e406ab5..0c79fe17 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -500,13 +500,8 @@ }, # "GINFES": { - "AUTORIZACAO": "GerarNfse", - "CANCELAR": "CancelarNfse", - "CONSULTA_RPS": "ConsultarNfsePorRps", - "CONSULTA_FAIXA": "ConsultarNfseFaixa", - "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", - "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", - "SUBSTITUIR": "SubstituirNfse", + "CONSULTA": "ConsultarNfseV3", + "CONSULTA_RPS": "ConsultarNfsePorRpsV3", "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl", "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl", }, From e812e5851ba6d120f390a2981a5c0d6e7d4d95fc Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:14:55 -0300 Subject: [PATCH 075/175] =?UTF-8?q?atualiza=20URLs=20de=20servi=C3=A7o=20d?= =?UTF-8?q?a=20GINFES=20para=20remover=20a=20extens=C3=A3o=20WSDL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/utils/webservices.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 0c79fe17..2da93dc0 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -502,8 +502,8 @@ "GINFES": { "CONSULTA": "ConsultarNfseV3", "CONSULTA_RPS": "ConsultarNfsePorRpsV3", - "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl", - "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl", + "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl", + "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl", }, "OSASCO": { "AUTORIZACAO": "Emitir", From cd4741fc76526a1205265b88eaa802c311e2e0da Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:21:58 -0300 Subject: [PATCH 076/175] =?UTF-8?q?atualiza=20m=C3=A9todos=20de=20comunica?= =?UTF-8?q?=C3=A7=C3=A3o=20da=20NFS-e=20para=20utilizar=20a=20biblioteca?= =?UTF-8?q?=20zeep=20e=20ajusta=20URLs=20de=20servi=C3=A7o=20da=20GINFES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 37 ++++++++++++++++-------------- pynfe/utils/webservices.py | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 75b9708f..9a4dc477 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -803,7 +803,7 @@ def consultar_rps(self, xml): # comunica via wsdl return self._post(url, xml, "consultaRps") elif self.autorizador == "GINFES": - return self._post_https(url, xml, "consultaRps") + return self._zeep_client(url, xml, "consultaRps") elif self.autorizador == "OSASCO": # comunica via wsdl return self._zeep_client(url, xml, "Consultar") @@ -961,6 +961,9 @@ def _post_https(self, url, xml, metodo): elif metodo == "enviar_lote": return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) elif metodo == "consulta": + print("URL:", url) + print("Cabecalho:", cabecalho) + print("XML:", xml) return cliente.service.ConsultarNfseV3(cabecalho, xml) elif metodo == "consulta_lote": return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) @@ -1011,7 +1014,7 @@ def _post_barueri_requests(self, url, xml, operation): Recebe o envelope SOAP completo já montado """ import requests - + certificado_a1 = CertificadoA1(self.certificado) try: chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) @@ -1027,26 +1030,23 @@ def _post_barueri_requests(self, url, xml, operation): soap_action = "http://www.barueri.sp.gov.br/nfe/NFeLoteBaixarArquivo" else: raise Exception(f"Operação {operation} não implementada para Barueri.") - - headers = { - 'Content-Type': 'text/xml; charset=utf-8', - 'SOAPAction': f'"{soap_action}"' - } - + + headers = {"Content-Type": "text/xml; charset=utf-8", "SOAPAction": f'"{soap_action}"'} + return requests.post( url, - data=xml.encode('utf-8'), + data=xml.encode("utf-8"), headers=headers, cert=chave_cert, verify=False, - timeout=30 + timeout=30, ) - + except requests.exceptions.RequestException as e: raise e finally: certificado_a1.excluir() - + def _post_barueri_https(self, url, xml, metodo): """ LEGACY: Comunicação wsdl (https) utilizando certificado do usuário @@ -1087,12 +1087,12 @@ def _post_barueri_https(self, url, xml, metodo): def enviar_sp(self, xml, operation, versao_schema=2): """ Send XML to São Paulo NFS-e webservice. - + Args: xml: XML string to send operation: Operation name (enviar_rps, teste_envio_lote_rps, envio_lote_rps, consultar_rps, cancelar) versao_schema: Schema version (1 for v1, 2 for v2 - Reforma Tributária 2026) - + Returns: WebService response """ @@ -1105,10 +1105,10 @@ def enviar_sp(self, xml, operation, versao_schema=2): def _post_sp_https(self, url, xml, metodo, versao_schema=2): """ Comunicação wsdl (https) utilizando certificado do usuário. - + According to São Paulo NFS-e manual v3.3.4: - VersaoSchema=2: Schema version 2 (Reforma Tributária 2026) - + Args: url: WebService URL xml: XML message string @@ -1129,7 +1129,9 @@ def _post_sp_https(self, url, xml, metodo, versao_schema=2): if metodo == "enviar_rps": return cliente.service.EnvioRPS(VersaoSchema=versao_schema, MensagemXML=xml) elif metodo == "teste_envio_lote_rps": - return cliente.service.TesteEnvioLoteRPS(VersaoSchema=versao_schema, MensagemXML=xml) + return cliente.service.TesteEnvioLoteRPS( + VersaoSchema=versao_schema, MensagemXML=xml + ) elif metodo == "envio_lote_rps": return cliente.service.EnvioLoteRPS(VersaoSchema=versao_schema, MensagemXML=xml) elif metodo == "consultar_rps": @@ -1142,6 +1144,7 @@ def _post_sp_https(self, url, xml, metodo, versao_schema=2): raise e finally: certificadoA1.excluir() + def _zeep_client(self, wsdl, payload, metodo, wcf_compatibility=True): """Comunicação wsdl utilizando a biblioteca zeep""" diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 2da93dc0..0c79fe17 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -502,8 +502,8 @@ "GINFES": { "CONSULTA": "ConsultarNfseV3", "CONSULTA_RPS": "ConsultarNfsePorRpsV3", - "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl", - "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl", + "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl", + "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl", }, "OSASCO": { "AUTORIZACAO": "Emitir", From 8bee3a0f80b2696c199c83ef6b8681a5fa34e9f4 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:24:01 -0300 Subject: [PATCH 077/175] =?UTF-8?q?remove=20extens=C3=A3o=20=3Fwsdl=20da?= =?UTF-8?q?=20URL=20do=20cliente=20e=20ajusta=20a=20cria=C3=A7=C3=A3o=20do?= =?UTF-8?q?=20objeto=20Client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 9a4dc477..d4c0c2bc 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -953,7 +953,20 @@ def _post_https(self, url, xml, metodo): certificadoA1 = CertificadoA1(self.certificado) chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) - cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) + wsdl = url # ?wsdl + endpoint = url.replace("?wsdl", "") # REMOVE ?wsdl + + print("WSDL:", wsdl) + print("ENDPOINT:", endpoint) + + cliente = Client( + wsdl, + transport=HttpAuthenticated( + key=chave, + cert=cert, + endereco=endpoint # ✅ endpoint correto + ) + ) # gerar nfse if metodo == "gerar": From 6b6ab35e0500afffc3c0223328d4ba6caff6749e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:34:17 -0300 Subject: [PATCH 078/175] =?UTF-8?q?ajusta=20m=C3=A9todos=20de=20comunica?= =?UTF-8?q?=C3=A7=C3=A3o=20da=20NFS-e=20para=20incluir=20consulta=20comple?= =?UTF-8?q?ta=20e=20modifica=20a=20implementa=C3=A7=C3=A3o=20para=20usar?= =?UTF-8?q?=20a=20biblioteca=20zeep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 122 +++++++++++++++++------------ pynfe/utils/webservices.py | 1 + 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index d4c0c2bc..e300f592 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -6,26 +6,14 @@ import requests from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils import etree, so_numeros -from pynfe.utils.flags import ( - CODIGOS_ESTADOS, - MODELO_MDFE, - NAMESPACE_BETHA, - NAMESPACE_CTE, - NAMESPACE_CTE_METODO, - NAMESPACE_MDFE, - NAMESPACE_MDFE_METODO, - NAMESPACE_METODO, - NAMESPACE_NFCOM, - NAMESPACE_NFCOM_METODO, - NAMESPACE_NFE, - NAMESPACE_SOAP, - NAMESPACE_XSD, - NAMESPACE_XSI, - VERSAO_CTE, - VERSAO_MDFE, - VERSAO_NFCOM, - VERSAO_PADRAO, -) +from pynfe.utils.flags import (CODIGOS_ESTADOS, MODELO_MDFE, NAMESPACE_BETHA, + NAMESPACE_CTE, NAMESPACE_CTE_METODO, + NAMESPACE_MDFE, NAMESPACE_MDFE_METODO, + NAMESPACE_METODO, NAMESPACE_NFCOM, + NAMESPACE_NFCOM_METODO, NAMESPACE_NFE, + NAMESPACE_SOAP, NAMESPACE_XSD, NAMESPACE_XSI, + VERSAO_CTE, VERSAO_MDFE, VERSAO_NFCOM, + VERSAO_PADRAO) from pynfe.utils.webservices import CTE, MDFE, NFCE, NFCOM, NFE, NFSE from .assinatura import AssinaturaA1 @@ -782,17 +770,19 @@ def enviar_lote(self, xml): else: raise Exception("Este método só esta implementado no autorizador ginfes.") - def consultar(self, payload): + def consultar(self, xml): # url do serviço url = self._get_url() + if self.autorizador == "GINFES": - # xml - payload = '' + payload + cabecalho = self._cabecalho_ginfes() + xml = '' + xml # comunica via wsdl - return self._post_https(url, payload, "consulta") + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"],cabecalho, xml) + elif self.autorizador == "OSASCO": # comunica via wsdl - return self._zeep_client(url, payload, "ConsultarNotaCompleta") + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_COMPLETA"], xml) else: raise Exception("Este método não esta implementado para o autorizador.") @@ -803,10 +793,12 @@ def consultar_rps(self, xml): # comunica via wsdl return self._post(url, xml, "consultaRps") elif self.autorizador == "GINFES": - return self._zeep_client(url, xml, "consultaRps") + cabecalho = self._cabecalho_ginfes() + xml = '' + xml + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._zeep_client(url, xml, "Consultar") + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) else: raise Exception("Este método não esta implementado para o autorizador.") @@ -818,7 +810,7 @@ def consultar_faixa(self, xml): return self._post(url, xml, "consultaFaixa") elif self.autorizador == "OSASCO": # comunica via wsdl - return self._zeep_client(url, xml, "Consultar") + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) else: raise Exception("Este método não esta implementado para o autorizador.") @@ -953,7 +945,7 @@ def _post_https(self, url, xml, metodo): certificadoA1 = CertificadoA1(self.certificado) chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) - wsdl = url # ?wsdl + wsdl = url # ?wsdl endpoint = url.replace("?wsdl", "") # REMOVE ?wsdl print("WSDL:", wsdl) @@ -962,10 +954,8 @@ def _post_https(self, url, xml, metodo): cliente = Client( wsdl, transport=HttpAuthenticated( - key=chave, - cert=cert, - endereco=endpoint # ✅ endpoint correto - ) + key=chave, cert=cert, endereco=endpoint # ✅ endpoint correto + ), ) # gerar nfse @@ -1158,32 +1148,66 @@ def _post_sp_https(self, url, xml, metodo, versao_schema=2): finally: certificadoA1.excluir() - def _zeep_client(self, wsdl, payload, metodo, wcf_compatibility=True): - """Comunicação wsdl utilizando a biblioteca zeep""" - - # comunicacao wsdl + def _post_zeep(self, wsdl, metodo, *args, wcf_compatibility=True): + """ + Comunicação wsdl utilizando a biblioteca zeep (GINFES compatível) + + Ex: + _post_zeep( + wsdl, + "ConsultarNfseV3", + cabecalho_xml, + xml_consulta + ) + """ try: + import requests from zeep import Client from zeep.helpers import serialize_object from zeep.settings import Settings from zeep.transports import Transport - + session = requests.Session() - - transport = Transport(session=session, timeout=60) + session.verify = False + + # certificado A1 + if self.certificado: + certificadoA1 = CertificadoA1(self.certificado) + chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) + session.cert = (cert, chave) + + transport = Transport( + session=session, + timeout=60 + ) settings = Settings( - strict=not wcf_compatibility, xml_huge_tree=True # IMPORTANTÍSSIMO p/ WCF + strict=not wcf_compatibility, + xml_huge_tree=True ) - client = Client(wsdl=wsdl, transport=transport, settings=settings) - if hasattr(client.service, metodo): - service = getattr(client.service, metodo) - return serialize_object(service(payload)) - else: - raise Exception("Método não implementado no autorizador.") - except Exception as e: - raise e + client = Client( + wsdl=wsdl, + transport=transport, + settings=settings + ) + + # DEBUG ÚTIL + print("SOAP endpoint:", client.service._binding_options["address"]) + + if not hasattr(client.service, metodo): + raise Exception(f"Método {metodo} não existe no WSDL") + + service = getattr(client.service, metodo) + + # chama com N parâmetros (GINFES usa 2) + response = service(*args) + + # GINFES retorna string XML + return serialize_object(response) + + finally: + certificadoA1.excluir() class ComunicacaoMDFe(Comunicacao): diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 0c79fe17..c1294151 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -511,6 +511,7 @@ "CANCELAR": "Cancelar", "CANCELAR_LOTE": "CancelarNotaLote", "CONSULTA": "Consultar", + "CONSULTA_COMPLETA": "ConsultarNotaCompleta", "HTTPS": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", "HOMOLOGACAO": ( "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl" From 41a3131367f6d1258e3109db12d37c38e2d02b70 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:37:47 -0300 Subject: [PATCH 079/175] =?UTF-8?q?adiciona=20logs=20de=20depura=C3=A7?= =?UTF-8?q?=C3=A3o=20para=20o=20m=C3=A9todo=20de=20comunica=C3=A7=C3=A3o?= =?UTF-8?q?=20da=20NFS-e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index e300f592..68df9d9e 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1194,10 +1194,14 @@ def _post_zeep(self, wsdl, metodo, *args, wcf_compatibility=True): # DEBUG ÚTIL print("SOAP endpoint:", client.service._binding_options["address"]) - + + if not hasattr(client.service, metodo): raise Exception(f"Método {metodo} não existe no WSDL") - + + print("Chamando método:", metodo) + print("Parâmetros:", args) + service = getattr(client.service, metodo) # chama com N parâmetros (GINFES usa 2) From 3917d178a3f04d1ca3d56388116f3484c9118282 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:46:11 -0300 Subject: [PATCH 080/175] =?UTF-8?q?remove=20cabe=C3=A7alho=20XML=20desnece?= =?UTF-8?q?ss=C3=A1rio=20e=20logs=20de=20depura=C3=A7=C3=A3o=20na=20classe?= =?UTF-8?q?=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 68df9d9e..6940d0d5 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -776,7 +776,6 @@ def consultar(self, xml): if self.autorizador == "GINFES": cabecalho = self._cabecalho_ginfes() - xml = '' + xml # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"],cabecalho, xml) @@ -794,7 +793,6 @@ def consultar_rps(self, xml): return self._post(url, xml, "consultaRps") elif self.autorizador == "GINFES": cabecalho = self._cabecalho_ginfes() - xml = '' + xml return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -948,8 +946,6 @@ def _post_https(self, url, xml, metodo): wsdl = url # ?wsdl endpoint = url.replace("?wsdl", "") # REMOVE ?wsdl - print("WSDL:", wsdl) - print("ENDPOINT:", endpoint) cliente = Client( wsdl, @@ -964,9 +960,6 @@ def _post_https(self, url, xml, metodo): elif metodo == "enviar_lote": return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) elif metodo == "consulta": - print("URL:", url) - print("Cabecalho:", cabecalho) - print("XML:", xml) return cliente.service.ConsultarNfseV3(cabecalho, xml) elif metodo == "consulta_lote": return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) From 5e6a2d99ec000e05a904f061c7ebab543bb068ce Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:51:27 -0300 Subject: [PATCH 081/175] =?UTF-8?q?adiciona=20codifica=C3=A7=C3=A3o=20UTF-?= =?UTF-8?q?8=20nas=20chamadas=20de=20toxml=20para=20serializa=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20NFS-e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 4bf16c76..e96c168a 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -102,7 +102,7 @@ def gerar(self, nfse): gnfse.Rps = declaracao_servico gnfse = ( - gnfse.toxml(element_name="GerarNfseEnvio") + gnfse.toxml(encoding="utf-8", element_name="GerarNfseEnvio") .replace("ns1:", "") .replace(":ns1", "") .replace('', "") @@ -130,7 +130,7 @@ def consultar_rps(self, nfse): consulta.Prestador = id_prestador consulta = ( - consulta.toxml(element_name="ConsultarNfseRpsEnvio") + consulta.toxml(encoding="utf-8", element_name="ConsultarNfseRpsEnvio") .replace("ns1:", "") .replace(":ns1", "") .replace('', "") @@ -156,7 +156,7 @@ def consultar_faixa(self, emitente, inicio, fim, pagina): consulta.Faixa.NumeroNfseFinal = fim consulta = ( - consulta.toxml(element_name="ConsultarNfseFaixaEnvio") + consulta.toxml(encoding="utf-8", element_name="ConsultarNfseFaixaEnvio") .replace("ns1:", "") .replace(":ns1", "") .replace('', "") @@ -189,7 +189,7 @@ def cancelar(self, nfse): cancelar = nfse_schema.CancelarNfseEnvio() cancelar.Pedido = pedido - return cancelar.toxml(element_name="CancelarNfseEnvio") + return cancelar.toxml(encoding="utf-8", element_name="CancelarNfseEnvio") def serializar_lote_sincrono(self, nfse): """Retorna string de um XML gerado a partir do @@ -268,7 +268,7 @@ def serializar_lote_sincrono(self, nfse): gnfse = nfse_schema.EnviarLoteRpsSincronoEnvio() gnfse.LoteRps = lote - return gnfse.toxml(element_name="EnviarLoteRpsSincronoEnvio") + return gnfse.toxml(encoding="utf-8", element_name="EnviarLoteRpsSincronoEnvio") class SerializacaoGinfes(InterfaceAutorizador): @@ -321,7 +321,7 @@ def consultar_rps(self, emitente, numero, serie, tipo): consulta.IdentificacaoRps = id_rps consulta.Prestador = id_prestador - return consulta.toxml(element_name="ns1:ConsultarNfseRpsEnvio") + return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseRpsEnvio") def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): # Prestador @@ -340,7 +340,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim - return consulta.toxml(element_name="ns1:ConsultarNfseEnvio") + return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio" ) def consultar_lote(self, emitente, numero): # Prestador @@ -352,7 +352,7 @@ def consultar_lote(self, emitente, numero): consulta.Prestador = id_prestador consulta.Protocolo = str(numero) - return consulta.toxml(element_name="ns1:ConsultarLoteRpsEnvio") + return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarLoteRpsEnvio") def consultar_situacao_lote(self, emitente, numero): "Serializa lote de envio, baseado no servico_consultar_situacao_lote_rps_envio_v03.xsd" @@ -365,7 +365,7 @@ def consultar_situacao_lote(self, emitente, numero): consulta.Prestador = id_prestador consulta.Protocolo = str(numero) - return consulta.toxml(element_name="ns1:ConsultarSituacaoLoteRpsEnvio") + return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarSituacaoLoteRpsEnvio") def serializar_lote_assincrono(self, nfse): "Serializa lote de envio, baseado no servico_enviar_lote_rps_envio_v03.xsd" @@ -504,7 +504,7 @@ def serializar_lote_assincrono(self, nfse): enviarLote = servico_enviar_lote_rps_envio_v03.EnviarLoteRpsEnvio() enviarLote.LoteRps = lote - return enviarLote.toxml(element_name="ns1:EnviarLoteRpsEnvio") + return enviarLote.toxml(encoding="utf-8", element_name="ns1:EnviarLoteRpsEnvio") def cancelar(self, nfse, codigo): """Retorna string de um XML gerado a partir do @@ -530,7 +530,7 @@ def cancelar(self, nfse, codigo): cancelar = servico_cancelar_nfse_envio_v03.CancelarNfseEnvio() cancelar.Pedido = pedido - return cancelar.toxml(element_name="ns1:CancelarNfseEnvio") + return cancelar.toxml(encoding="utf-8", element_name="ns1:CancelarNfseEnvio") def cancelar_v2(self, nfse): # serialização utilizando lxml @@ -552,4 +552,4 @@ def cabecalho(self): cabecalho = cabecalho_v03.cabecalho() cabecalho.versao = "3" cabecalho.versaoDados = "3" - return cabecalho.toxml(element_name="ns2:cabecalho") + return cabecalho.toxml(encoding="utf-8", element_name="ns2:cabecalho") From 0f420dc38b21cda344b05a32b922753d736dac94 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 18:56:59 -0300 Subject: [PATCH 082/175] =?UTF-8?q?adiciona=20cabe=C3=A7alho=20e=20corpo?= =?UTF-8?q?=20XML=20para=20consulta=20de=20NFS-e=20no=20m=C3=A9todo=20de?= =?UTF-8?q?=20comunica=C3=A7=C3=A3o=20da=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 6940d0d5..e5f7b0cf 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -775,7 +775,30 @@ def consultar(self, xml): url = self._get_url() if self.autorizador == "GINFES": - cabecalho = self._cabecalho_ginfes() + cabecalho = """ + + 3 + + """.strip() + + xml = """ + + + + 13743550000819 + 4073347 + + + + 2024-01-01 + 2024-01-31 + + + + """.strip() + # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"],cabecalho, xml) From 1c3a33c7664302e16c527aef9345a9f41e87b99e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 19:04:04 -0300 Subject: [PATCH 083/175] =?UTF-8?q?substitui=20cabe=C3=A7alho=20e=20corpo?= =?UTF-8?q?=20XML=20fixos=20por=20m=C3=A9todo=20para=20gera=C3=A7=C3=A3o?= =?UTF-8?q?=20do=20cabe=C3=A7alho=20na=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index e5f7b0cf..6940d0d5 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -775,30 +775,7 @@ def consultar(self, xml): url = self._get_url() if self.autorizador == "GINFES": - cabecalho = """ - - 3 - - """.strip() - - xml = """ - - - - 13743550000819 - 4073347 - - - - 2024-01-01 - 2024-01-31 - - - - """.strip() - + cabecalho = self._cabecalho_ginfes() # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"],cabecalho, xml) From 5f4a642691f711634baaec6a6570454ae0386d08 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:21:51 -0300 Subject: [PATCH 084/175] =?UTF-8?q?adiciona=20namespace=20ao=20cabe=C3=A7a?= =?UTF-8?q?lho=20XML=20na=20serializa=C3=A7=C3=A3o=20da=20classe=20Seriali?= =?UTF-8?q?zacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e96c168a..42771e91 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -552,4 +552,8 @@ def cabecalho(self): cabecalho = cabecalho_v03.cabecalho() cabecalho.versao = "3" cabecalho.versaoDados = "3" - return cabecalho.toxml(encoding="utf-8", element_name="ns2:cabecalho") + return cabecalho.toxml( + encoding="utf-8", + element_name="cabecalho", + namespace="http://www.ginfes.com.br/tipos_v03.xsd" + ) From 98b1a0fb734c679743f2505d6f6c3c701338a909 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:25:45 -0300 Subject: [PATCH 085/175] =?UTF-8?q?remove=20namespace=20do=20cabe=C3=A7alh?= =?UTF-8?q?o=20XML=20na=20serializa=C3=A7=C3=A3o=20da=20classe=20Serializa?= =?UTF-8?q?caoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 42771e91..0e43d9a7 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -554,6 +554,5 @@ def cabecalho(self): cabecalho.versaoDados = "3" return cabecalho.toxml( encoding="utf-8", - element_name="cabecalho", - namespace="http://www.ginfes.com.br/tipos_v03.xsd" + element_name="cabecalho" ) From d4524462cc9e3f11282ab48e4f5b5e3308cc9092 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:33:34 -0300 Subject: [PATCH 086/175] =?UTF-8?q?remove=20verifica=C3=A7=C3=A3o=20desnec?= =?UTF-8?q?ess=C3=A1ria=20ao=20buscar=20refer=C3=AAncia=20no=20m=C3=A9todo?= =?UTF-8?q?=20assinar=20da=20classe=20AssinaturaA1=20e=20adiciona=20gera?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20UUID=20na=20consulta=20de=20NFS-e=20na=20c?= =?UTF-8?q?lasse=20SerializacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 2 +- pynfe/processamento/autorizador_nfse.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index b057078d..50c5e263 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -29,7 +29,7 @@ def __init__(self, certificado, senha): def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.find(".//*[@Id]").attrib["Id"] if xml.find(".//*[@Id]") is not None else None + reference = xml.find(".//*[@Id]").attrib["Id"] # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 0e43d9a7..ac083e0d 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -1,6 +1,6 @@ from pyxb import BIND from importlib import import_module - +import uuid class InterfaceAutorizador: # TODO Colocar raise Exception Not Implemented nos metodos @@ -331,6 +331,8 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio() consulta.Prestador = id_prestador + + consulta.Id = str(uuid.uuid4()) # Consulta por Numero if numero is not None: consulta.NumeroNfse = numero From bcc4c4d6c7678b77bef4a7d8ec399a142461c7cb Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:43:29 -0300 Subject: [PATCH 087/175] =?UTF-8?q?adiciona=20atributo=20Id=20na=20classe?= =?UTF-8?q?=20CTD=5FANON=20e=20ajusta=20a=20gera=C3=A7=C3=A3o=20do=20XML?= =?UTF-8?q?=20na=20classe=20SerializacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 5 ++--- .../nfse/ginfes/servico_consultar_nfse_envio_v03.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index ac083e0d..391e6d93 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -331,8 +331,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio() consulta.Prestador = id_prestador - - consulta.Id = str(uuid.uuid4()) + consulta.Id = str(uuid.uuid4()) # Consulta por Numero if numero is not None: consulta.NumeroNfse = numero @@ -342,7 +341,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim - return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio" ) + return consulta.toxml(encoding="utf-8", element_name="ConsultarNfseEnvio" ) def consultar_lote(self, emitente, numero): # Prestador diff --git a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py index 06e1d8fb..517a04dc 100644 --- a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py +++ b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py @@ -233,7 +233,17 @@ class CTD_ANON_(pyxb.binding.basis.complexTypeDefinition): "/workspaces/PyNFe/pynfe/data/XSDs/NFS-e/Ginfes/servico_consultar_nfse_envio_v03.xsd", 12, 5 ) _ElementMap = {} + __Id = pyxb.binding.content.AttributeUse( + pyxb.namespace.ExpandedName(None, "Id"), + "Id", + "__httpwww_ginfes_com_brservico_consultar_nfse_envio_v03_xsd_CTD_ANON_Id", + pyxb.binding.datatypes.ID, + required=False, + ) + + Id = property(__Id.value, __Id.set, None, None) _AttributeMap = {} + _AttributeMap.update({__Id.name(): __Id}) # Base type is pyxb.binding.datatypes.anyType # Element {http://www.ginfes.com.br/servico_consultar_nfse_envio_v03.xsd}DataInicial uses Python identifier DataInicial From 5878e0afbe136e483003c04c3e7f9b30f8c0c2af Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:47:16 -0300 Subject: [PATCH 088/175] =?UTF-8?q?substitui=20busca=20de=20atributo=20Id?= =?UTF-8?q?=20na=20classe=20AssinaturaA1=20para=20uso=20de=20xpath=20e=20a?= =?UTF-8?q?justa=20formata=C3=A7=C3=A3o=20do=20retorno=20na=20classe=20Ser?= =?UTF-8?q?ializacaoOsasco?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 2 +- pynfe/processamento/autorizador_nfse.py | 60 +++++++++++++++---------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 50c5e263..70b3b856 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -29,7 +29,7 @@ def __init__(self, certificado, senha): def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.find(".//*[@Id]").attrib["Id"] + reference = xml.xpath('//*[@Id]')[0].attrib['Id'] # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 391e6d93..2ae6c978 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -1,6 +1,8 @@ -from pyxb import BIND -from importlib import import_module import uuid +from importlib import import_module + +from pyxb import BIND + class InterfaceAutorizador: # TODO Colocar raise Exception Not Implemented nos metodos @@ -10,23 +12,36 @@ def consultar_rps(self): def cancelar(self): pass + class SerializacaoOsasco: def __init__(self, chave_autenticacao): self.chave_autenticacao = chave_autenticacao - - def consultar(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None, numero_rps_unico=None): + + def consultar( + self, + cnpj_tomador=None, + cpf_tomador=None, + data_inicial=None, + data_final=None, + numero_nota_inicial=None, + numero_nota_final=None, + numero_rps_inicial=None, + numero_rps_final=None, + numero_rps_unico=None, + ): return { - "ChaveAutenticacao": self.chave_autenticacao, - "CNPJTomador": cnpj_tomador, - "CPFTomador": cpf_tomador, - "DataInicial": data_inicial, - "DataFinal": data_final, - "NumeroNotaInicial": numero_nota_inicial, - "NumeroNotaFinal": numero_nota_final, - "NumeroReciboInicial": numero_rps_inicial, - "NumeroReciboFinal": numero_rps_final, - "NumeroReciboUnico": numero_rps_unico, - } + "ChaveAutenticacao": self.chave_autenticacao, + "CNPJTomador": cnpj_tomador, + "CPFTomador": cpf_tomador, + "DataInicial": data_inicial, + "DataFinal": data_final, + "NumeroNotaInicial": numero_nota_inicial, + "NumeroNotaFinal": numero_nota_final, + "NumeroReciboInicial": numero_rps_inicial, + "NumeroReciboFinal": numero_rps_final, + "NumeroReciboUnico": numero_rps_unico, + } + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): @@ -340,8 +355,8 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao = BIND() consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim - - return consulta.toxml(encoding="utf-8", element_name="ConsultarNfseEnvio" ) + print("ID no objeto:", consulta.Id) + return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio") def consultar_lote(self, emitente, numero): # Prestador @@ -542,9 +557,9 @@ def cancelar_v2(self, nfse): raiz = etree.Element("{%s}CancelarNfseEnvio" % ns1, nsmap={"ns1": ns1, "ns2": ns2}) prestador = etree.SubElement(raiz, "{%s}Prestador" % ns1) etree.SubElement(prestador, "{%s}Cnpj" % ns2).text = nfse.emitente.cnpj - etree.SubElement( - prestador, "{%s}InscricaoMunicipal" % ns2 - ).text = nfse.emitente.inscricao_municipal + etree.SubElement(prestador, "{%s}InscricaoMunicipal" % ns2).text = ( + nfse.emitente.inscricao_municipal + ) etree.SubElement(raiz, "{%s}NumeroNfse" % ns1).text = nfse.identificador return etree.tostring(raiz, encoding="unicode") @@ -553,7 +568,4 @@ def cabecalho(self): cabecalho = cabecalho_v03.cabecalho() cabecalho.versao = "3" cabecalho.versaoDados = "3" - return cabecalho.toxml( - encoding="utf-8", - element_name="cabecalho" - ) + return cabecalho.toxml(encoding="utf-8", element_name="cabecalho") From f621a9c5ba974424f7b6d543f93d0085867f5bec Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:48:04 -0300 Subject: [PATCH 089/175] =?UTF-8?q?remove=20o=20nome=20do=20elemento=20no?= =?UTF-8?q?=20retorno=20da=20serializa=C3=A7=C3=A3o=20na=20classe=20Serial?= =?UTF-8?q?izacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 2ae6c978..a14141cd 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -356,7 +356,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim print("ID no objeto:", consulta.Id) - return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio") + return consulta.toxml(encoding="utf-8") def consultar_lote(self, emitente, numero): # Prestador From f5ea5a4e451d9ebdb4988622e7846c8fa3509959 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:50:09 -0300 Subject: [PATCH 090/175] adiciona atributo Id no elemento ConsultarNfseEnvio para suporte a XMLDSig --- .../nfse/ginfes/servico_consultar_nfse_envio_v03.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py index 517a04dc..395166e7 100644 --- a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py +++ b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py @@ -293,7 +293,18 @@ class CTD_ANON_(pyxb.binding.basis.complexTypeDefinition): Namespace.addCategoryObject( "elementBinding", ConsultarNfseEnvio.name().localName(), ConsultarNfseEnvio ) +# === Atributo Id no ELEMENTO (obrigatório para XMLDSig) === +ConsultarNfseEnvio._AttributeMap = getattr(ConsultarNfseEnvio, "_AttributeMap", {}) + +__Id = pyxb.binding.content.AttributeUse( + pyxb.namespace.ExpandedName(None, "Id"), + "Id", + "__httpwww_ginfes_com_brservico_consultar_nfse_envio_v03_xsd_ConsultarNfseEnvio_Id", + pyxb.binding.datatypes.ID, + required=False, +) +ConsultarNfseEnvio._AttributeMap.update({__Id.name(): __Id}) CTD_ANON._AddElement( pyxb.binding.basis.element( From 1a3f63cf97d44d4f9f2bbb66a1f7d641dfe2652d Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:52:14 -0300 Subject: [PATCH 091/175] =?UTF-8?q?adiciona=20gera=C3=A7=C3=A3o=20de=20UUI?= =?UTF-8?q?D=20no=20construtor=20de=20ConsultarNfseEnvio=20na=20classe=20S?= =?UTF-8?q?erializacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index a14141cd..1d731d32 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -344,9 +344,8 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): id_prestador.Cnpj = emitente.cnpj id_prestador.InscricaoMunicipal = emitente.inscricao_municipal - consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio() + consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio(Id=str(uuid.uuid4())) consulta.Prestador = id_prestador - consulta.Id = str(uuid.uuid4()) # Consulta por Numero if numero is not None: consulta.NumeroNfse = numero From 93f98c9ede9b2a0fa1548b80760c8e9d0610fb90 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:55:28 -0300 Subject: [PATCH 092/175] =?UTF-8?q?ajusta=20a=20gera=C3=A7=C3=A3o=20do=20X?= =?UTF-8?q?ML=20na=20classe=20SerializacaoGinfes=20para=20incluir=20atribu?= =?UTF-8?q?to=20Id=20com=20valor=20=C3=BAnico?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 16 +++++++++++--- .../servico_consultar_nfse_envio_v03.py | 21 ------------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 1d731d32..30548982 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -1,6 +1,6 @@ import uuid from importlib import import_module - +from lxml import etree from pyxb import BIND @@ -344,7 +344,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): id_prestador.Cnpj = emitente.cnpj id_prestador.InscricaoMunicipal = emitente.inscricao_municipal - consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio(Id=str(uuid.uuid4())) + consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio() consulta.Prestador = id_prestador # Consulta por Numero if numero is not None: @@ -355,7 +355,17 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim print("ID no objeto:", consulta.Id) - return consulta.toxml(encoding="utf-8") + + xml = consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio") + root = etree.fromstring(xml) + root.attrib["Id"] = f"CNFSE{uuid.uuid4().hex.upper()}" + + xml = etree.tostring( + root, + encoding="utf-8", + xml_declaration=True + ) + return xml def consultar_lote(self, emitente, numero): # Prestador diff --git a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py index 395166e7..06e1d8fb 100644 --- a/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py +++ b/pynfe/utils/nfse/ginfes/servico_consultar_nfse_envio_v03.py @@ -233,17 +233,7 @@ class CTD_ANON_(pyxb.binding.basis.complexTypeDefinition): "/workspaces/PyNFe/pynfe/data/XSDs/NFS-e/Ginfes/servico_consultar_nfse_envio_v03.xsd", 12, 5 ) _ElementMap = {} - __Id = pyxb.binding.content.AttributeUse( - pyxb.namespace.ExpandedName(None, "Id"), - "Id", - "__httpwww_ginfes_com_brservico_consultar_nfse_envio_v03_xsd_CTD_ANON_Id", - pyxb.binding.datatypes.ID, - required=False, - ) - - Id = property(__Id.value, __Id.set, None, None) _AttributeMap = {} - _AttributeMap.update({__Id.name(): __Id}) # Base type is pyxb.binding.datatypes.anyType # Element {http://www.ginfes.com.br/servico_consultar_nfse_envio_v03.xsd}DataInicial uses Python identifier DataInicial @@ -293,18 +283,7 @@ class CTD_ANON_(pyxb.binding.basis.complexTypeDefinition): Namespace.addCategoryObject( "elementBinding", ConsultarNfseEnvio.name().localName(), ConsultarNfseEnvio ) -# === Atributo Id no ELEMENTO (obrigatório para XMLDSig) === -ConsultarNfseEnvio._AttributeMap = getattr(ConsultarNfseEnvio, "_AttributeMap", {}) - -__Id = pyxb.binding.content.AttributeUse( - pyxb.namespace.ExpandedName(None, "Id"), - "Id", - "__httpwww_ginfes_com_brservico_consultar_nfse_envio_v03_xsd_ConsultarNfseEnvio_Id", - pyxb.binding.datatypes.ID, - required=False, -) -ConsultarNfseEnvio._AttributeMap.update({__Id.name(): __Id}) CTD_ANON._AddElement( pyxb.binding.basis.element( From af265ba675cdf1a9c600bc68ad2f90228db63e65 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 22:55:56 -0300 Subject: [PATCH 093/175] adiciona atributo Id com UUID no elemento ConsultarNfseEnvio na classe SerializacaoGinfes --- pynfe/processamento/autorizador_nfse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 30548982..2ee72c7f 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -354,8 +354,7 @@ def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): consulta.PeriodoEmissao = BIND() consulta.PeriodoEmissao.DataInicial = inicio consulta.PeriodoEmissao.DataFinal = fim - print("ID no objeto:", consulta.Id) - + xml = consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio") root = etree.fromstring(xml) root.attrib["Id"] = f"CNFSE{uuid.uuid4().hex.upper()}" From 7e4255c585223ef594cf6dde8213b6c805e28705 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:08:40 -0300 Subject: [PATCH 094/175] =?UTF-8?q?adiciona=20m=C3=A9todo=20consultar=5Fnf?= =?UTF-8?q?se=5Ffaixa=20na=20classe=20SerializacaoGinfes=20para=20consulta?= =?UTF-8?q?=20de=20NFSe=20por=20faixa=20de=20n=C3=BAmeros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 52 +++++++++++++++---------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 2ee72c7f..6e3ef94f 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -338,33 +338,43 @@ def consultar_rps(self, emitente, numero, serie, tipo): return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseRpsEnvio") - def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): + def consultar_nfse_faixa(emitente, numero_inicial, numero_final, pagina=1): + NS = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" + DS = "http://www.w3.org/2000/09/xmldsig#" + + nsmap = { + None: NS, + "ds": DS + } + + root = etree.Element( + f"{{{NS}}}ConsultarNfseFaixaEnvio", + nsmap=nsmap + ) + + # Id obrigatório (ANTES da assinatura) + root.attrib["Id"] = f"CNFSEFAIXA{uuid.uuid4().hex.upper()}" + # Prestador - id_prestador = _tipos.tcIdentificacaoPrestador() - id_prestador.Cnpj = emitente.cnpj - id_prestador.InscricaoMunicipal = emitente.inscricao_municipal + prestador = etree.SubElement(root, f"{{{NS}}}Prestador") + cpf_cnpj = etree.SubElement(prestador, f"{{{NS}}}CpfCnpj") + etree.SubElement(cpf_cnpj, f"{{{NS}}}Cnpj").text = emitente.cnpj + etree.SubElement(prestador, f"{{{NS}}}InscricaoMunicipal").text = emitente.inscricao_municipal - consulta = servico_consultar_nfse_envio_v03.ConsultarNfseEnvio() - consulta.Prestador = id_prestador - # Consulta por Numero - if numero is not None: - consulta.NumeroNfse = numero - else: - # consulta por Data - consulta.PeriodoEmissao = BIND() - consulta.PeriodoEmissao.DataInicial = inicio - consulta.PeriodoEmissao.DataFinal = fim - - xml = consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseEnvio") - root = etree.fromstring(xml) - root.attrib["Id"] = f"CNFSE{uuid.uuid4().hex.upper()}" - - xml = etree.tostring( + # Faixa + faixa = etree.SubElement(root, f"{{{NS}}}Faixa") + etree.SubElement(faixa, f"{{{NS}}}NumeroNfseInicial").text = str(numero_inicial) + etree.SubElement(faixa, f"{{{NS}}}NumeroNfseFinal").text = str(numero_final) + + # Página + etree.SubElement(root, f"{{{NS}}}Pagina").text = str(pagina) + + return etree.tostring( root, encoding="utf-8", xml_declaration=True ) - return xml + def consultar_lote(self, emitente, numero): # Prestador From cdd9d4310499a266b4442f292e4558871b008ae8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:09:28 -0300 Subject: [PATCH 095/175] =?UTF-8?q?corrige=20assinatura=20do=20m=C3=A9todo?= =?UTF-8?q?=20consultar=5Fnfse=5Ffaixa=20na=20classe=20SerializacaoGinfes?= =?UTF-8?q?=20para=20incluir=20'self'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 6e3ef94f..993d15e9 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -338,7 +338,7 @@ def consultar_rps(self, emitente, numero, serie, tipo): return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseRpsEnvio") - def consultar_nfse_faixa(emitente, numero_inicial, numero_final, pagina=1): + def consultar_nfse_faixa(self, emitente, numero_inicial, numero_final, pagina=1): NS = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" DS = "http://www.w3.org/2000/09/xmldsig#" From 95dd8eb4da07282f40bef1412d01135fa6db27b2 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:11:36 -0300 Subject: [PATCH 096/175] =?UTF-8?q?renomeia=20m=C3=A9todo=20consultar=5Fnf?= =?UTF-8?q?se=20para=20consultar=5Ffaixa=20na=20classe=20SerializacaoGinfe?= =?UTF-8?q?s=20e=20ajusta=20chamada=20na=20classe=20SerializacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 2 +- pynfe/processamento/serializacao.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 993d15e9..348faf15 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -338,7 +338,7 @@ def consultar_rps(self, emitente, numero, serie, tipo): return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseRpsEnvio") - def consultar_nfse_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): NS = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" DS = "http://www.w3.org/2000/09/xmldsig#" diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index bd7d7e4a..1d5f221f 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2167,14 +2167,14 @@ def gerar_lote(self, nfse): return SerializacaoGinfes().serializar_lote_assincrono(nfse) else: raise Exception("Este método só esta implementado no autorizador ginfes.") - - def consultar_nfse(self, emitente, numero=None, inicio=None, fim=None): + + def consultar_faixa(self, cnpj_prestador, numero_nfse_inicial, numero_nfse_final): if self.autorizador.lower() == "ginfes": from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - return SerializacaoGinfes().consultar_nfse(emitente, numero, inicio, fim) + + return SerializacaoGinfes().consultar_faixa(cnpj_prestador=cnpj_prestador, numero_nfse_inicial=numero_nfse_inicial, numero_nfse_final=numero_nfse_final) else: - raise Exception("Este método só esta implementado no autorizador ginfes.") + raise Exception("Este método só esta implementado no autorizador Osasco.") def consultar_nota_emitida(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None): if self.autorizador.lower() == "osasco": From 5faa0f5e03b2c292684887256e0e85ddd11f8da0 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:13:13 -0300 Subject: [PATCH 097/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20consultar=5Ffaix?= =?UTF-8?q?a=20na=20classe=20SerializacaoNfse=20para=20aceitar=20novos=20p?= =?UTF-8?q?ar=C3=A2metros=20e=20atualizar=20chamada=20na=20classe=20Serial?= =?UTF-8?q?izacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/serializacao.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 1d5f221f..f9711636 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2168,11 +2168,11 @@ def gerar_lote(self, nfse): else: raise Exception("Este método só esta implementado no autorizador ginfes.") - def consultar_faixa(self, cnpj_prestador, numero_nfse_inicial, numero_nfse_final): + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): if self.autorizador.lower() == "ginfes": from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - return SerializacaoGinfes().consultar_faixa(cnpj_prestador=cnpj_prestador, numero_nfse_inicial=numero_nfse_inicial, numero_nfse_final=numero_nfse_final) + return SerializacaoGinfes().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) else: raise Exception("Este método só esta implementado no autorizador Osasco.") From 096647cf81579ea1b2e6b9f9d267a040b8c752ab Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:26:47 -0300 Subject: [PATCH 098/175] =?UTF-8?q?ajusta=20m=C3=A9todos=20de=20consulta?= =?UTF-8?q?=20na=20classe=20SerializacaoGinfes=20para=20incluir=20novos=20?= =?UTF-8?q?servi=C3=A7os=20e=20par=C3=A2metros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 308 ++++-------------------- pynfe/processamento/comunicacao.py | 7 +- pynfe/processamento/serializacao.py | 60 +---- pynfe/utils/webservices.py | 10 +- 4 files changed, 64 insertions(+), 321 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 348faf15..90343ffa 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -1,5 +1,6 @@ import uuid from importlib import import_module + from lxml import etree from pyxb import BIND @@ -288,69 +289,44 @@ def serializar_lote_sincrono(self, nfse): class SerializacaoGinfes(InterfaceAutorizador): def __init__(self): - # importa - global _tipos, servico_consultar_nfse_envio_v03 - global servico_enviar_lote_rps_envio_v03, cabecalho_v03 - global servico_cancelar_nfse_envio_v03 - global servico_consultar_lote_rps_envio_v03 - global servico_consultar_situacao_lote_rps_envio_v03 - global servico_consultar_nfse_rps_envio_v03 - _tipos = import_module("pynfe.utils.nfse.ginfes._tipos") - servico_consultar_nfse_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_consultar_nfse_envio_v03" - ) - servico_cancelar_nfse_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_cancelar_nfse_envio_v03" - ) - servico_enviar_lote_rps_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_enviar_lote_rps_envio_v03" - ) - cabecalho_v03 = import_module("pynfe.utils.nfse.ginfes.cabecalho_v03") - servico_consultar_lote_rps_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_consultar_lote_rps_envio_v03" - ) - servico_consultar_situacao_lote_rps_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_consultar_situacao_lote_rps_envio_v03" - ) - servico_consultar_nfse_rps_envio_v03 = import_module( - "pynfe.utils.nfse.ginfes.servico_consultar_nfse_rps_envio_v03" - ) + pass + + def consultar_servico_prestado(self, emitente, data_inicio, data_fim, pagina=1): + NS = "http://www.ginfes.com.br/servico_consultar_nfse_servico_prestado_envio_v03.xsd" + DS = "http://www.w3.org/2000/09/xmldsig#" - def consultar_rps(self, emitente, numero, serie, tipo): - """Retorna string de um XML de consulta por Rps gerado a partir do - XML Schema (XSD). Binding gerado pelo modulo PyXB. - servico_consultar_nfse_rps_envio_v03.xsd - """ - # Rps - id_rps = _tipos.tcIdentificacaoRps() - id_rps.Numero = numero - id_rps.Serie = serie - id_rps.Tipo = tipo + nsmap = {None: NS, "ds": DS} + + root = etree.Element(f"{{{NS}}}ConsultarNfseServicoPrestadoEnvio", nsmap=nsmap) + + # ID obrigatório (antes da assinatura) + root.attrib["Id"] = f"CNFSESP{uuid.uuid4().hex.upper()}" # Prestador - id_prestador = _tipos.tcIdentificacaoPrestador() - id_prestador.Cnpj = emitente.cnpj - id_prestador.InscricaoMunicipal = emitente.inscricao_municipal + prestador = etree.SubElement(root, f"{{{NS}}}Prestador") + cpf_cnpj = etree.SubElement(prestador, f"{{{NS}}}CpfCnpj") + etree.SubElement(cpf_cnpj, f"{{{NS}}}Cnpj").text = emitente.cnpj + etree.SubElement(prestador, f"{{{NS}}}InscricaoMunicipal").text = ( + emitente.inscricao_municipal + ) - consulta = servico_consultar_nfse_rps_envio_v03.ConsultarNfseRpsEnvio() - consulta.IdentificacaoRps = id_rps - consulta.Prestador = id_prestador + # Período + periodo = etree.SubElement(root, f"{{{NS}}}PeriodoEmissao") + etree.SubElement(periodo, f"{{{NS}}}DataInicial").text = data_inicio + etree.SubElement(periodo, f"{{{NS}}}DataFinal").text = data_fim + + # Página + etree.SubElement(root, f"{{{NS}}}Pagina").text = str(pagina) - return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarNfseRpsEnvio") + return etree.tostring(root, encoding="utf-8", xml_declaration=True) def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): NS = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" DS = "http://www.w3.org/2000/09/xmldsig#" - nsmap = { - None: NS, - "ds": DS - } + nsmap = {None: NS, "ds": DS} - root = etree.Element( - f"{{{NS}}}ConsultarNfseFaixaEnvio", - nsmap=nsmap - ) + root = etree.Element(f"{{{NS}}}ConsultarNfseFaixaEnvio", nsmap=nsmap) # Id obrigatório (ANTES da assinatura) root.attrib["Id"] = f"CNFSEFAIXA{uuid.uuid4().hex.upper()}" @@ -359,7 +335,9 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): prestador = etree.SubElement(root, f"{{{NS}}}Prestador") cpf_cnpj = etree.SubElement(prestador, f"{{{NS}}}CpfCnpj") etree.SubElement(cpf_cnpj, f"{{{NS}}}Cnpj").text = emitente.cnpj - etree.SubElement(prestador, f"{{{NS}}}InscricaoMunicipal").text = emitente.inscricao_municipal + etree.SubElement(prestador, f"{{{NS}}}InscricaoMunicipal").text = ( + emitente.inscricao_municipal + ) # Faixa faixa = etree.SubElement(root, f"{{{NS}}}Faixa") @@ -369,221 +347,17 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): # Página etree.SubElement(root, f"{{{NS}}}Pagina").text = str(pagina) - return etree.tostring( - root, - encoding="utf-8", - xml_declaration=True - ) - - - def consultar_lote(self, emitente, numero): - # Prestador - id_prestador = _tipos.tcIdentificacaoPrestador() - id_prestador.Cnpj = emitente.cnpj - id_prestador.InscricaoMunicipal = emitente.inscricao_municipal - - consulta = servico_consultar_lote_rps_envio_v03.ConsultarLoteRpsEnvio() - consulta.Prestador = id_prestador - consulta.Protocolo = str(numero) - - return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarLoteRpsEnvio") + return etree.tostring(root, encoding="utf-8", xml_declaration=True) - def consultar_situacao_lote(self, emitente, numero): - "Serializa lote de envio, baseado no servico_consultar_situacao_lote_rps_envio_v03.xsd" - # Prestador - id_prestador = _tipos.tcIdentificacaoPrestador() - id_prestador.Cnpj = emitente.cnpj - id_prestador.InscricaoMunicipal = emitente.inscricao_municipal - - consulta = servico_consultar_situacao_lote_rps_envio_v03.ConsultarSituacaoLoteRpsEnvio() - consulta.Prestador = id_prestador - consulta.Protocolo = str(numero) - - return consulta.toxml(encoding="utf-8", element_name="ns1:ConsultarSituacaoLoteRpsEnvio") - - def serializar_lote_assincrono(self, nfse): - "Serializa lote de envio, baseado no servico_enviar_lote_rps_envio_v03.xsd" - - servico = _tipos.tcDadosServico() - valores_servico = _tipos.tcValores() - valores_servico.ValorServicos = nfse.servico.valor_servico - # valores_servico.ValorServicos = str(Decimal( - # nfse.servico.valor_servico.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))) - valores_servico.IssRetido = nfse.servico.iss_retido - # Dados opcionais - if nfse.servico.valor_deducoes: - valores_servico.ValorDeducoes = nfse.servico.valor_deducoes - if nfse.servico.valor_pis: - valores_servico.ValorPis = nfse.servico.valor_pis - if nfse.servico.valor_confins: - valores_servico.ValorCofins = nfse.servico.valor_confins - if nfse.servico.valor_inss: - valores_servico.ValorInss = nfse.servico.valor_inss - if nfse.servico.valor_ir: - valores_servico.ValorIr = nfse.servico.valor_ir - if nfse.servico.valor_csll: - valores_servico.ValorCsll = nfse.servico.valor_csll - if nfse.servico.valor_iss: - valores_servico.ValorIss = nfse.servico.valor_iss - if nfse.servico.valor_iss_retido: - valores_servico.ValorIssRetido = nfse.servico.valor_iss_retido - if nfse.servico.valor_liquido: - valores_servico.ValorLiquidoNfse = nfse.servico.valor_liquido - if nfse.servico.outras_retencoes: - valores_servico.OutrasRetencoes = nfse.servico.outras_retencoes - if nfse.servico.base_calculo: - valores_servico.BaseCalculo = nfse.servico.base_calculo - if nfse.servico.aliquota: - valores_servico.Aliquota = nfse.servico.aliquota - if nfse.servico.desconto_incondicionado: - valores_servico.DescontoIncondicionado = nfse.servico.desconto_incondicionado - if nfse.servico.desconto_condicionado: - valores_servico.DescontoCondicionado = nfse.servico.desconto_condicionado - - servico.Valores = valores_servico - servico.ItemListaServico = nfse.servico.item_lista - # opcionais - if nfse.servico.codigo_cnae: - servico.CodigoCnae = nfse.servico.codigo_cnae - if nfse.servico.codigo_tributacao_municipio: - servico.CodigoTributacaoMunicipio = nfse.servico.codigo_tributacao_municipio - # obrigatórios - servico.Discriminacao = nfse.servico.discriminacao - servico.CodigoMunicipio = nfse.servico.codigo_municipio - - # endereco tomador - endereco_tomador = _tipos.tcEndereco() - endereco_tomador.Endereco = nfse.cliente.endereco_logradouro - if nfse.cliente.endereco_complemento: - endereco_tomador.Complemento = nfse.cliente.endereco_complemento - endereco_tomador.Numero = nfse.cliente.endereco_numero - endereco_tomador.Bairro = nfse.cliente.endereco_bairro - if nfse.cliente.endereco_cod_municipio: - endereco_tomador.CodigoMunicipio = nfse.cliente.endereco_cod_municipio - endereco_tomador.Uf = nfse.cliente.endereco_uf - endereco_tomador.Cep = nfse.cliente.endereco_cep - # identificacao Tomador - id_tomador = _tipos.tcIdentificacaoTomador() - id_tomador.CpfCnpj = nfse.cliente.numero_documento - if nfse.cliente.inscricao_municipal: - id_tomador.InscricaoMunicipal = nfse.cliente.inscricao_municipal - # Tomador - tomador = _tipos.tcDadosTomador() - tomador.IdentificacaoTomador = id_tomador - tomador.RazaoSocial = nfse.cliente.razao_social - tomador.Endereco = endereco_tomador - # opcional - if nfse.cliente.endereco_telefone or nfse.cliente.email: - tomador.Contato = _tipos.tcContato() - if nfse.cliente.endereco_telefone: - tomador.Contato.Telefone = nfse.cliente.endereco_telefone - if nfse.cliente.email: - tomador.Contato.Email = nfse.cliente.email - - # Prestador - id_prestador = _tipos.tcIdentificacaoPrestador() - id_prestador.Cnpj = nfse.emitente.cnpj - id_prestador.InscricaoMunicipal = nfse.emitente.inscricao_municipal - - # identificacao rps - id_rps = _tipos.tcIdentificacaoRps() - id_rps.Numero = nfse.identificador - id_rps.Serie = nfse.serie - id_rps.Tipo = nfse.tipo - # inf rps - inf_rps = _tipos.tcInfRps() - inf_rps.IdentificacaoRps = id_rps - inf_rps.DataEmissao = nfse.data_emissao.strftime("%Y-%m-%dT%H:%M:%S") - # Natureza da Operação - # 1 – Tributação no município - # 2 - Tributação fora do município - # 3 - Isenção - # 4 - Imune - # 5 –Exigibilidade suspensa por decisão judicial - # 6 – Exigibilidade suspensa por procedimento administrativo - inf_rps.NaturezaOperacao = nfse.natureza_operacao - # Regime Especial de Tributação - # 1 – Microempresa municipal - # 2 - Estimativa - # 3 – Sociedade de profissionais - # 4 – Cooperativa - # 5 - Microempresário Individual (MEI) - # 6 - Microempresário e Empresa de Pequeno Porte (ME EPP) - if nfse.regime_especial: - inf_rps.RegimeEspecialTributacao = nfse.regime_especial - inf_rps.OptanteSimplesNacional = nfse.simples # 1-sim 2-nao - inf_rps.IncentivadorCultural = nfse.incentivo # 1-sim 2-nao - # Código de status da NFS-e - # 1-Normal 2-Cancelado (sempre 1, nota não pode ser enviada como cancelada) - inf_rps.Status = 1 - inf_rps.RpsSubstituido = None # opcional - inf_rps.Servico = servico - inf_rps.Prestador = id_prestador - inf_rps.Tomador = tomador - inf_rps.IntermediarioServico = None # opcional - inf_rps.ConstrucaoCivil = None # opcional - inf_rps.Id = nfse.identificador - - rps = _tipos.tcRps() - rps.InfRps = inf_rps - - lote = _tipos.tcLoteRps() - lote.NumeroLote = 1 - lote.Id = 1 - lote.Cnpj = nfse.emitente.cnpj - lote.InscricaoMunicipal = nfse.emitente.inscricao_municipal - lote.QuantidadeRps = 1 - lote.ListaRps = BIND() - lote.ListaRps.append(rps) - - enviarLote = servico_enviar_lote_rps_envio_v03.EnviarLoteRpsEnvio() - enviarLote.LoteRps = lote - return enviarLote.toxml(encoding="utf-8", element_name="ns1:EnviarLoteRpsEnvio") - - def cancelar(self, nfse, codigo): - """Retorna string de um XML gerado a partir do - XML Schema (XSD). Binding gerado pelo modulo PyXB.""" - # id nfse - id_nfse = _tipos.tcIdentificacaoNfse() - id_nfse.Numero = nfse.identificador - id_nfse.Cnpj = nfse.emitente.cnpj - id_nfse.InscricaoMunicipal = nfse.emitente.inscricao_municipal - id_nfse.CodigoMunicipio = nfse.emitente.endereco_cod_municipio - - # Info Pedido de cancelamento - info_pedido = _tipos.tcInfPedidoCancelamento() - info_pedido.Id = "1" - info_pedido.IdentificacaoNfse = id_nfse - info_pedido.CodigoCancelamento = codigo - - # Pedido - pedido = _tipos.tcPedidoCancelamento() - pedido.InfPedidoCancelamento = info_pedido - - # Cancelamento - cancelar = servico_cancelar_nfse_envio_v03.CancelarNfseEnvio() - cancelar.Pedido = pedido + def cabecalho(self): + NS = "http://www.ginfes.com.br/cabecalho_v03.xsd" - return cancelar.toxml(encoding="utf-8", element_name="ns1:CancelarNfseEnvio") + nsmap = {None: NS} - def cancelar_v2(self, nfse): - # serialização utilizando lxml - from lxml import etree + cabecalho = etree.Element(f"{{{NS}}}cabecalho", nsmap=nsmap) + cabecalho.attrib["versao"] = "3" + versao_dados = etree.SubElement(cabecalho, f"{{{NS}}}versaoDados") + versao_dados.text = "3" - ns1 = "http://www.ginfes.com.br/servico_cancelar_nfse_envio" - ns2 = "http://www.ginfes.com.br/tipos" - raiz = etree.Element("{%s}CancelarNfseEnvio" % ns1, nsmap={"ns1": ns1, "ns2": ns2}) - prestador = etree.SubElement(raiz, "{%s}Prestador" % ns1) - etree.SubElement(prestador, "{%s}Cnpj" % ns2).text = nfse.emitente.cnpj - etree.SubElement(prestador, "{%s}InscricaoMunicipal" % ns2).text = ( - nfse.emitente.inscricao_municipal - ) - etree.SubElement(raiz, "{%s}NumeroNfse" % ns1).text = nfse.identificador - return etree.tostring(raiz, encoding="unicode") - - def cabecalho(self): - # info - cabecalho = cabecalho_v03.cabecalho() - cabecalho.versao = "3" - cabecalho.versaoDados = "3" - return cabecalho.toxml(encoding="utf-8", element_name="cabecalho") + return etree.tostring(cabecalho, encoding="utf-8", xml_declaration=True) + diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 6940d0d5..732ddb91 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -777,7 +777,7 @@ def consultar(self, xml): if self.autorizador == "GINFES": cabecalho = self._cabecalho_ginfes() # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"],cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -806,9 +806,12 @@ def consultar_faixa(self, xml): if self.autorizador == "BETHA": # comunica via wsdl return self._post(url, xml, "consultaFaixa") + elif self.autorizador == "GINFES": + cabecalho = self._cabecalho_ginfes() + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) else: raise Exception("Este método não esta implementado para o autorizador.") diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index f9711636..f454d5db 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -3,29 +3,15 @@ import hashlib import re import warnings - from datetime import datetime import pynfe.utils.xml_writer as xmlw from pynfe.entidades import Manifesto, NotaFiscal -from pynfe.utils import ( - etree, - obter_codigo_por_municipio, - obter_municipio_por_codigo, - obter_pais_por_codigo, - so_numeros, -) -from pynfe.utils.flags import ( - CODIGOS_ESTADOS, - NAMESPACE_CTE, - NAMESPACE_MDFE, - NAMESPACE_NFE, - NAMESPACE_SIG, - VERSAO_CTE, - VERSAO_MDFE, - VERSAO_PADRAO, - VERSAO_QRCODE, -) +from pynfe.utils import (etree, obter_codigo_por_municipio, + obter_municipio_por_codigo, obter_pais_por_codigo, + so_numeros) +from pynfe.utils.flags import (CODIGOS_ESTADOS, NAMESPACE_CTE, NAMESPACE_MDFE, + NAMESPACE_NFE, NAMESPACE_SIG, VERSAO_MDFE, VERSAO_PADRAO, VERSAO_QRCODE) from pynfe.utils.webservices import MDFE, NFCE @@ -2160,21 +2146,21 @@ def gerar(self, nfse): else: raise Exception("Este método só esta implementado no autorizador Betha.") - def gerar_lote(self, nfse): + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): if self.autorizador.lower() == "ginfes": from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - return SerializacaoGinfes().serializar_lote_assincrono(nfse) + + return SerializacaoGinfes().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) else: raise Exception("Este método só esta implementado no autorizador ginfes.") - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + def consultar_servico_prestado(self, emitente, data_inicio, data_fim, pagina=1): if self.autorizador.lower() == "ginfes": from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - return SerializacaoGinfes().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) + return SerializacaoGinfes().consultar_servico_prestado(emitente, data_inicio, data_fim, pagina=1) else: - raise Exception("Este método só esta implementado no autorizador Osasco.") + raise Exception("Este método só esta implementado no autorizador ginfes.") def consultar_nota_emitida(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None): if self.autorizador.lower() == "osasco": @@ -2192,31 +2178,9 @@ def consultar_lote(self, emitente, numero): else: raise Exception("Este método só esta implementado no autorizador ginfes.") - def consultar_rps(self, emitente, numero, serie, tipo): - if self.autorizador.lower() == "ginfes": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - return SerializacaoGinfes().consultar_rps(emitente, numero, serie, tipo) - else: - raise Exception("Este método só esta implementado no autorizador ginfes.") - - def consultar_situacao_lote(self, emitente, numero): - if self.autorizador.lower() == "ginfes": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - return SerializacaoGinfes().consultar_situacao_lote(emitente, numero) - else: - raise Exception("Este método só esta implementado no autorizador ginfes.") def cancelar(self, nfse): - if self.autorizador.lower() == "ginfes": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - # versao 3 - # return SerializacaoGinfes().cancelar(nfse) - # versao 2 - return SerializacaoGinfes().cancelar_v2(nfse) - elif self.autorizador.lower() == "betha": + if self.autorizador.lower() == "betha": from pynfe.processamento.autorizador_nfse import SerializacaoBetha return SerializacaoBetha().cancelar(nfse) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index c1294151..c9929a28 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -500,16 +500,18 @@ }, # "GINFES": { - "CONSULTA": "ConsultarNfseV3", - "CONSULTA_RPS": "ConsultarNfsePorRpsV3", + "AUTORIZACAO": "GerarNfse", + "CANCELAR": "CancelarNfse", + "CONSULTA_RPS": "consultarNfsePorRps", + "CONSULTA_FAIXA": "ConsultarNfseFaixa", + "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", + "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", "HTTPS": "https://producao.ginfes.com.br/ServiceGinfesImpl?wsdl", "HOMOLOGACAO": "https://homologacao.ginfes.com.br/ServiceGinfesImpl?wsdl", }, "OSASCO": { "AUTORIZACAO": "Emitir", - "AUTORIZACAO_LOTE": "EmitirEmLote", "CANCELAR": "Cancelar", - "CANCELAR_LOTE": "CancelarNotaLote", "CONSULTA": "Consultar", "CONSULTA_COMPLETA": "ConsultarNotaCompleta", "HTTPS": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", From a368abdc370a4a519d6942536343ffd037920a11 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:49:53 -0300 Subject: [PATCH 099/175] =?UTF-8?q?adiciona=20suporte=20ao=20autorizador?= =?UTF-8?q?=20Campinas=20com=20m=C3=A9todos=20de=20consulta=20e=20configur?= =?UTF-8?q?a=C3=A7=C3=A3o=20de=20namespace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 108 +++++++++++++++++++++++- pynfe/processamento/comunicacao.py | 89 ++++++------------- pynfe/processamento/serializacao.py | 50 ++++------- pynfe/utils/webservices.py | 10 +++ 4 files changed, 157 insertions(+), 100 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 90343ffa..0f4175a5 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -286,7 +286,7 @@ def serializar_lote_sincrono(self, nfse): return gnfse.toxml(encoding="utf-8", element_name="EnviarLoteRpsSincronoEnvio") - + class SerializacaoGinfes(InterfaceAutorizador): def __init__(self): pass @@ -360,4 +360,108 @@ def cabecalho(self): versao_dados.text = "3" return etree.tostring(cabecalho, encoding="utf-8", xml_declaration=True) - + + +class SerializacaoCampinas(InterfaceAutorizador): + + NS = "http://www.abrasf.org.br/nfse.xsd" + DS = "http://www.w3.org/2000/09/xmldsig#" + + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + nsmap = { + None: self.NS, + "ds": self.DS, + } + + root = etree.Element( + f"{{{self.NS}}}ConsultarNfseServicoPrestadoEnvio", + nsmap=nsmap + ) + + # ID é OPCIONAL no XSD, mas Campinas aceita + root.attrib["Id"] = f"CNFSESP{uuid.uuid4().hex}" + + # Prestador + prestador = etree.SubElement(root, f"{{{self.NS}}}Prestador") + + cpf_cnpj = etree.SubElement(prestador, f"{{{self.NS}}}CpfCnpj") + etree.SubElement(cpf_cnpj, f"{{{self.NS}}}Cnpj").text = emitente.cnpj + + etree.SubElement( + prestador, + f"{{{self.NS}}}InscricaoMunicipal" + ).text = emitente.inscricao_municipal + + # Período de emissão + periodo = etree.SubElement(root, f"{{{self.NS}}}PeriodoEmissao") + etree.SubElement(periodo, f"{{{self.NS}}}DataInicial").text = data_inicio + etree.SubElement(periodo, f"{{{self.NS}}}DataFinal").text = data_fim + + # Página (mínimo 1) + etree.SubElement(root, f"{{{self.NS}}}Pagina").text = str(pagina) + + # Signature OBRIGATÓRIA no XSD + etree.SubElement(root, f"{{{self.DS}}}Signature") + + return etree.tostring( + root, + encoding="utf-8", + xml_declaration=True + ) + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + nsmap = { + None: self.NS, + "ds": self.DS, + } + + root = etree.Element( + f"{{{self.NS}}}ConsultarNfseFaixaEnvio", + nsmap=nsmap + ) + + root.attrib["Id"] = f"CNFSEFAIXA{uuid.uuid4().hex}" + + prestador = etree.SubElement(root, f"{{{self.NS}}}Prestador") + cpf_cnpj = etree.SubElement(prestador, f"{{{self.NS}}}CpfCnpj") + etree.SubElement(cpf_cnpj, f"{{{self.NS}}}Cnpj").text = emitente.cnpj + etree.SubElement( + prestador, + f"{{{self.NS}}}InscricaoMunicipal" + ).text = emitente.inscricao_municipal + + faixa = etree.SubElement(root, f"{{{self.NS}}}Faixa") + etree.SubElement( + faixa, + f"{{{self.NS}}}NumeroNfseInicial" + ).text = str(numero_inicial) + + if numero_final: + etree.SubElement( + faixa, + f"{{{self.NS}}}NumeroNfseFinal" + ).text = str(numero_final) + + etree.SubElement(root, f"{{{self.NS}}}Pagina").text = str(pagina) + + etree.SubElement(root, f"{{{self.DS}}}Signature") + + return etree.tostring( + root, + encoding="utf-8", + xml_declaration=True + ) + def cabecalho(self): + NS = "http://www.abrasf.org.br/nfse.xsd" + nsmap = {None: NS} + + cabecalho = etree.Element("cabecalho", nsmap=nsmap) + cabecalho.attrib["versao"] = "2.03" + + versao_dados = etree.SubElement(cabecalho, "versaoDados") + versao_dados.text = "2.03" + + return etree.tostring( + cabecalho, + encoding="utf-8", + xml_declaration=False + ) \ No newline at end of file diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 732ddb91..aef65ce6 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -745,41 +745,17 @@ def __init__(self, autorizador, certificado=None, certificado_senha=None, homolo elif self.autorizador == "OSASCO": self._namespace = "" self._versao = "1" + elif self.autorizador == "CAMPINAS": + self._namespace = "http://www.abrasf.org.br/nfse.xsd" + self._versao = "2" else: raise Exception("Autorizador não encontrado!") - def autorizacao(self, nota): + def consultar_nfse(self, xml): # url do serviço url = self._get_url() - if self.autorizador == "BETHA": - # xml - xml = etree.tostring(nota, encoding="unicode", pretty_print=False) - # comunica via wsdl - return self._post(url, xml, "gerar") - else: - raise Exception("Este método só esta implementado no autorizador betha.") - def enviar_lote(self, xml): - # url do serviço - url = self._get_url() - if self.autorizador == "GINFES": - # xml - xml = '' + xml - # comunica via wsdl - return self._post_https(url, xml, "enviar_lote") - else: - raise Exception("Este método só esta implementado no autorizador ginfes.") - - def consultar(self, xml): - # url do serviço - url = self._get_url() - - if self.autorizador == "GINFES": - cabecalho = self._cabecalho_ginfes() - # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"],cabecalho, xml) - - elif self.autorizador == "OSASCO": + if self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_COMPLETA"], xml) else: @@ -788,12 +764,10 @@ def consultar(self, xml): def consultar_rps(self, xml): # url do serviço url = self._get_url() - if self.autorizador == "BETHA": - # comunica via wsdl - return self._post(url, xml, "consultaRps") - elif self.autorizador == "GINFES": - cabecalho = self._cabecalho_ginfes() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"],cabecalho, xml) + if self.autorizador == "CAMPINAS": + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + cabecalho = SerializacaoCampinas().cabecalho() + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) @@ -803,11 +777,9 @@ def consultar_rps(self, xml): def consultar_faixa(self, xml): # url do serviço url = self._get_url() - if self.autorizador == "BETHA": - # comunica via wsdl - return self._post(url, xml, "consultaFaixa") - elif self.autorizador == "GINFES": - cabecalho = self._cabecalho_ginfes() + if self.autorizador == "CAMPINAS": + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + cabecalho = SerializacaoCampinas().cabecalho() return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -815,40 +787,27 @@ def consultar_faixa(self, xml): else: raise Exception("Este método não esta implementado para o autorizador.") - def consultar_lote(self, xml): + def consultar_periodo(self, xml): # url do serviço url = self._get_url() - if self.autorizador == "GINFES": - # xml - xml = '' + xml + if self.autorizador == "CAMPINAS": + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + cabecalho = SerializacaoCampinas().cabecalho() + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"],cabecalho, xml) + elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_https(url, xml, "consulta_lote") + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) else: raise Exception("Este método não esta implementado para o autorizador.") + def consultar_lote(self, xml): + raise Exception("Este método não esta implementado para o autorizador.") + def consultar_situacao_lote(self, xml): - # url do serviço - url = self._get_url() - if self.autorizador == "GINFES": - # comunica via wsdl - return self._post_https(url, xml, "consulta_situacao_lote") - else: - raise Exception("Este método não esta implementado para o autorizador.") + raise Exception("Este método não esta implementado para o autorizador.") def cancelar(self, xml): - # url do serviço - url = self._get_url() - # Betha - if self.autorizador == "BETHA": - # comunica via wsdl - return self._post(url, xml, "cancelar") - # Ginfes - elif self.autorizador == "GINFES": - # comunica via wsdl com certificado - return self._post_https(url, xml, "cancelar") - # TODO outros autorizadores - else: - raise Exception("Este método não esta implementado para o autorizador.") + raise Exception("Este método não esta implementado para o autorizador.") def _cabecalho(self, retorna_string=True): """Monta o XML do cabeçalho da requisição wsdl diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index f454d5db..756aad25 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2138,54 +2138,38 @@ def __init__(self, autorizador, chave_autenticacao=None): self.autorizador = autorizador self.chave_autenticacao = chave_autenticacao - def gerar(self, nfse): - if self.autorizador.lower() == "betha": - from pynfe.processamento.autorizador_nfse import SerializacaoBetha - - return SerializacaoBetha().gerar(nfse) - else: - raise Exception("Este método só esta implementado no autorizador Betha.") - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - if self.autorizador.lower() == "ginfes": + if self.autorizador.lower() == "campinas": from pynfe.processamento.autorizador_nfse import SerializacaoGinfes return SerializacaoGinfes().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) + elif self.autorizador.lower() == "osasco": + from pynfe.processamento.autorizador_nfse import SerializacaoOsasco + return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, numero_nota_inicial=numero_inicial, numero_nota_final=numero_final) else: - raise Exception("Este método só esta implementado no autorizador ginfes.") + raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") - def consultar_servico_prestado(self, emitente, data_inicio, data_fim, pagina=1): - if self.autorizador.lower() == "ginfes": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + if self.autorizador.lower() == "campinas": + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - return SerializacaoGinfes().consultar_servico_prestado(emitente, data_inicio, data_fim, pagina=1) - else: - raise Exception("Este método só esta implementado no autorizador ginfes.") - - def consultar_nota_emitida(self, cnpj_tomador=None, cpf_tomador=None, data_inicial=None, data_final=None, numero_nota_inicial=None, numero_nota_final=None, numero_rps_inicial=None, numero_rps_final=None): - if self.autorizador.lower() == "osasco": + return SerializacaoCampinas().consultar_periodo(emitente, data_inicio, data_fim, pagina=1) + elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco - return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=cnpj_tomador, cpf_tomador=cpf_tomador, data_inicial=data_inicial, data_final=data_final, numero_nota_inicial=numero_nota_inicial, numero_nota_final=numero_nota_final, numero_rps_inicial=numero_rps_inicial, numero_rps_final=numero_rps_final) + return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, data_inicial=data_inicio, data_final=data_fim) else: - raise Exception("Este método só esta implementado no autorizador Osasco.") + raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") - def consultar_lote(self, emitente, numero): - if self.autorizador.lower() == "ginfes": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - return SerializacaoGinfes().consultar_lote(emitente, numero) + def consultar_nfse(self, emitente, numero_nfse): + if self.autorizador.lower() == "osasco": + from pynfe.processamento.autorizador_nfse import SerializacaoOsasco + return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, numero_nota_inicial=numero_nfse, numero_nota_final=numero_nfse) else: - raise Exception("Este método só esta implementado no autorizador ginfes.") - + raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") - def cancelar(self, nfse): - if self.autorizador.lower() == "betha": - from pynfe.processamento.autorizador_nfse import SerializacaoBetha - return SerializacaoBetha().cancelar(nfse) - else: - raise Exception("Autorizador não suportado para cancelamento!") class SerializacaoQrcodeMDFe(object): diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index c9929a28..a97cada4 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -519,6 +519,16 @@ "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl" ), }, + "CAMPINAS": { + "AUTORIZACAO": "GerarNfse", + "CANCELAR": "CancelarNfse", + "CONSULTA_RPS": "consultarNfsePorRps", + "CONSULTA_FAIXA": "ConsultarNfseFaixa", + "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", + "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", + "HTTPS": "https://novanfse.campinas.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", + "HOMOLOGACAO": "https://homol-rps.ima.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", + }, } # MDF-e From 7ab16b27af31f83bb5cdcb1aed392b08c56cde2a Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:56:53 -0300 Subject: [PATCH 100/175] =?UTF-8?q?remove=20cabe=C3=A7alho=20das=20chamada?= =?UTF-8?q?s=20de=20consulta=20para=20o=20autorizador=20Campinas=20na=20cl?= =?UTF-8?q?asse=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index aef65ce6..2a707dc2 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -767,7 +767,7 @@ def consultar_rps(self, xml): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) @@ -780,7 +780,7 @@ def consultar_faixa(self, xml): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"],cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) From 0e912d5e6d4b2cbf2d461902b7932fafa0917270 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 29 Dec 2025 23:59:24 -0300 Subject: [PATCH 101/175] =?UTF-8?q?ajusta=20cabe=C3=A7alho=20nas=20chamada?= =?UTF-8?q?s=20de=20consulta=20para=20o=20autorizador=20Campinas=20na=20cl?= =?UTF-8?q?asse=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 2a707dc2..24bbc720 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -767,7 +767,7 @@ def consultar_rps(self, xml): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) @@ -780,7 +780,7 @@ def consultar_faixa(self, xml): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"],cabecalho, xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) @@ -1103,7 +1103,7 @@ def _post_sp_https(self, url, xml, metodo, versao_schema=2): finally: certificadoA1.excluir() - def _post_zeep(self, wsdl, metodo, *args, wcf_compatibility=True): + def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): """ Comunicação wsdl utilizando a biblioteca zeep (GINFES compatível) @@ -1155,14 +1155,11 @@ def _post_zeep(self, wsdl, metodo, *args, wcf_compatibility=True): raise Exception(f"Método {metodo} não existe no WSDL") print("Chamando método:", metodo) - print("Parâmetros:", args) + print("Parâmetros:", xml) service = getattr(client.service, metodo) - # chama com N parâmetros (GINFES usa 2) - response = service(*args) - - # GINFES retorna string XML + response = service(xml) return serialize_object(response) finally: From 37278bed90458091ac2cca9fc713506f7393b631 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:06:15 -0300 Subject: [PATCH 102/175] =?UTF-8?q?remove=20convers=C3=A3o=20para=20string?= =?UTF-8?q?=20em=20m=C3=A9todos=20da=20classe=20SerializacaoCampinas=20e?= =?UTF-8?q?=20adiciona=20logs=20de=20servi=C3=A7o=20na=20classe=20Comunica?= =?UTF-8?q?caoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 18 +++--------------- pynfe/processamento/comunicacao.py | 3 +++ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 0f4175a5..31ef21b2 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -403,11 +403,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): # Signature OBRIGATÓRIA no XSD etree.SubElement(root, f"{{{self.DS}}}Signature") - return etree.tostring( - root, - encoding="utf-8", - xml_declaration=True - ) + return root def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): nsmap = { None: self.NS, @@ -445,11 +441,7 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): etree.SubElement(root, f"{{{self.DS}}}Signature") - return etree.tostring( - root, - encoding="utf-8", - xml_declaration=True - ) + return root def cabecalho(self): NS = "http://www.abrasf.org.br/nfse.xsd" nsmap = {None: NS} @@ -460,8 +452,4 @@ def cabecalho(self): versao_dados = etree.SubElement(cabecalho, "versaoDados") versao_dados.text = "2.03" - return etree.tostring( - cabecalho, - encoding="utf-8", - xml_declaration=False - ) \ No newline at end of file + return cabecalho \ No newline at end of file diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 24bbc720..9590ce66 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1156,6 +1156,9 @@ def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): print("Chamando método:", metodo) print("Parâmetros:", xml) + print(client.wsdl.services) + print(client.wsdl.services[0].ports[0].operations) + print(client.service._binding._operations) service = getattr(client.service, metodo) From 5aa172c7802df55eb68247b378ff90ea3d7feb35 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:09:24 -0300 Subject: [PATCH 103/175] =?UTF-8?q?remove=20cabe=C3=A7alho=5Fxml=20das=20c?= =?UTF-8?q?hamadas=20no=20m=C3=A9todo=20de=20consulta=20da=20classe=20Comu?= =?UTF-8?q?nicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 9590ce66..3b519891 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1111,8 +1111,7 @@ def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): _post_zeep( wsdl, "ConsultarNfseV3", - cabecalho_xml, - xml_consulta + xml_consulta ou payload, ) """ try: @@ -1157,7 +1156,6 @@ def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): print("Chamando método:", metodo) print("Parâmetros:", xml) print(client.wsdl.services) - print(client.wsdl.services[0].ports[0].operations) print(client.service._binding._operations) service = getattr(client.service, metodo) From 6151a1b8618dd67e023ef6a08d5562203e9648f3 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:16:14 -0300 Subject: [PATCH 104/175] =?UTF-8?q?adiciona=20m=C3=A9todos=20de=20consulta?= =?UTF-8?q?=20na=20classe=20SerializacaoCampinas=20e=20ajusta=20chamadas?= =?UTF-8?q?=20na=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 121 ++++++------------------ pynfe/processamento/comunicacao.py | 114 ++++++++++------------ 2 files changed, 78 insertions(+), 157 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 31ef21b2..49ed69ae 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -43,6 +43,36 @@ def consultar( "NumeroReciboUnico": numero_rps_unico, } +class SerializacaoCampinas(InterfaceAutorizador): + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + return { + "Prestador": { + "CpfCnpj": { + "Cnpj": emitente.cnpj + }, + "InscricaoMunicipal": emitente.inscricao_municipal + }, + "PeriodoEmissao": { + "DataInicial": data_inicio, + "DataFinal": data_fim + }, + "Pagina": pagina + } + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + return { + "Prestador": { + "CpfCnpj": { + "Cnpj": emitente.cnpj + }, + "InscricaoMunicipal": emitente.inscricao_municipal + }, + "Faixa": { + "NumeroNfseInicial": numero_inicial, + "NumeroNfseFinal": numero_final + }, + "Pagina": pagina + } + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): @@ -362,94 +392,3 @@ def cabecalho(self): return etree.tostring(cabecalho, encoding="utf-8", xml_declaration=True) -class SerializacaoCampinas(InterfaceAutorizador): - - NS = "http://www.abrasf.org.br/nfse.xsd" - DS = "http://www.w3.org/2000/09/xmldsig#" - - def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - nsmap = { - None: self.NS, - "ds": self.DS, - } - - root = etree.Element( - f"{{{self.NS}}}ConsultarNfseServicoPrestadoEnvio", - nsmap=nsmap - ) - - # ID é OPCIONAL no XSD, mas Campinas aceita - root.attrib["Id"] = f"CNFSESP{uuid.uuid4().hex}" - - # Prestador - prestador = etree.SubElement(root, f"{{{self.NS}}}Prestador") - - cpf_cnpj = etree.SubElement(prestador, f"{{{self.NS}}}CpfCnpj") - etree.SubElement(cpf_cnpj, f"{{{self.NS}}}Cnpj").text = emitente.cnpj - - etree.SubElement( - prestador, - f"{{{self.NS}}}InscricaoMunicipal" - ).text = emitente.inscricao_municipal - - # Período de emissão - periodo = etree.SubElement(root, f"{{{self.NS}}}PeriodoEmissao") - etree.SubElement(periodo, f"{{{self.NS}}}DataInicial").text = data_inicio - etree.SubElement(periodo, f"{{{self.NS}}}DataFinal").text = data_fim - - # Página (mínimo 1) - etree.SubElement(root, f"{{{self.NS}}}Pagina").text = str(pagina) - - # Signature OBRIGATÓRIA no XSD - etree.SubElement(root, f"{{{self.DS}}}Signature") - - return root - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - nsmap = { - None: self.NS, - "ds": self.DS, - } - - root = etree.Element( - f"{{{self.NS}}}ConsultarNfseFaixaEnvio", - nsmap=nsmap - ) - - root.attrib["Id"] = f"CNFSEFAIXA{uuid.uuid4().hex}" - - prestador = etree.SubElement(root, f"{{{self.NS}}}Prestador") - cpf_cnpj = etree.SubElement(prestador, f"{{{self.NS}}}CpfCnpj") - etree.SubElement(cpf_cnpj, f"{{{self.NS}}}Cnpj").text = emitente.cnpj - etree.SubElement( - prestador, - f"{{{self.NS}}}InscricaoMunicipal" - ).text = emitente.inscricao_municipal - - faixa = etree.SubElement(root, f"{{{self.NS}}}Faixa") - etree.SubElement( - faixa, - f"{{{self.NS}}}NumeroNfseInicial" - ).text = str(numero_inicial) - - if numero_final: - etree.SubElement( - faixa, - f"{{{self.NS}}}NumeroNfseFinal" - ).text = str(numero_final) - - etree.SubElement(root, f"{{{self.NS}}}Pagina").text = str(pagina) - - etree.SubElement(root, f"{{{self.DS}}}Signature") - - return root - def cabecalho(self): - NS = "http://www.abrasf.org.br/nfse.xsd" - nsmap = {None: NS} - - cabecalho = etree.Element("cabecalho", nsmap=nsmap) - cabecalho.attrib["versao"] = "2.03" - - versao_dados = etree.SubElement(cabecalho, "versaoDados") - versao_dados.text = "2.03" - - return cabecalho \ No newline at end of file diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 3b519891..c337306b 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -6,14 +6,25 @@ import requests from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils import etree, so_numeros -from pynfe.utils.flags import (CODIGOS_ESTADOS, MODELO_MDFE, NAMESPACE_BETHA, - NAMESPACE_CTE, NAMESPACE_CTE_METODO, - NAMESPACE_MDFE, NAMESPACE_MDFE_METODO, - NAMESPACE_METODO, NAMESPACE_NFCOM, - NAMESPACE_NFCOM_METODO, NAMESPACE_NFE, - NAMESPACE_SOAP, NAMESPACE_XSD, NAMESPACE_XSI, - VERSAO_CTE, VERSAO_MDFE, VERSAO_NFCOM, - VERSAO_PADRAO) +from pynfe.utils.flags import ( + CODIGOS_ESTADOS, + MODELO_MDFE, + NAMESPACE_CTE, + NAMESPACE_CTE_METODO, + NAMESPACE_MDFE, + NAMESPACE_MDFE_METODO, + NAMESPACE_METODO, + NAMESPACE_NFCOM, + NAMESPACE_NFCOM_METODO, + NAMESPACE_NFE, + NAMESPACE_SOAP, + NAMESPACE_XSD, + NAMESPACE_XSI, + VERSAO_CTE, + VERSAO_MDFE, + VERSAO_NFCOM, + VERSAO_PADRAO, +) from pynfe.utils.webservices import CTE, MDFE, NFCE, NFCOM, NFE, NFSE from .assinatura import AssinaturaA1 @@ -731,13 +742,7 @@ def __init__(self, autorizador, certificado=None, certificado_senha=None, homolo self.certificado_senha = certificado_senha self._ambiente = 2 if homologacao else 1 self.autorizador = autorizador.upper() - if self.autorizador == "GINFES": - self._namespace = "http://www.ginfes.com.br/cabecalho_v03.xsd" - self._versao = "3" - elif self.autorizador == "BETHA": - self._namespace = NAMESPACE_BETHA - self._versao = "2.02" - elif self.autorizador == "SAO_PAULO": + if self.autorizador == "SAO_PAULO": self._namespace = "http://www.prefeitura.sp.gov.br/nfe" self._versao = "2" elif self.autorizador == "BARUERI": @@ -751,62 +756,59 @@ def __init__(self, autorizador, certificado=None, certificado_senha=None, homolo else: raise Exception("Autorizador não encontrado!") - def consultar_nfse(self, xml): + def consultar_nfse(self, payload): # url do serviço url = self._get_url() if self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_COMPLETA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_COMPLETA"], payload) else: raise Exception("Este método não esta implementado para o autorizador.") - def consultar_rps(self, xml): + def consultar_rps(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, payload) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) else: raise Exception("Este método não esta implementado para o autorizador.") - def consultar_faixa(self, xml): + def consultar_faixa(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"],cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) else: raise Exception("Este método não esta implementado para o autorizador.") - def consultar_periodo(self, xml): + def consultar_periodo(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"],cabecalho, xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], xml) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payloadpayload) else: raise Exception("Este método não esta implementado para o autorizador.") - def consultar_lote(self, xml): + def consultar_lote(self, payload): raise Exception("Este método não esta implementado para o autorizador.") - def consultar_situacao_lote(self, xml): + def consultar_situacao_lote(self, payload): raise Exception("Este método não esta implementado para o autorizador.") - def cancelar(self, xml): + def cancelar(self, payload): raise Exception("Este método não esta implementado para o autorizador.") def _cabecalho(self, retorna_string=True): @@ -908,7 +910,6 @@ def _post_https(self, url, xml, metodo): wsdl = url # ?wsdl endpoint = url.replace("?wsdl", "") # REMOVE ?wsdl - cliente = Client( wsdl, transport=HttpAuthenticated( @@ -1103,16 +1104,10 @@ def _post_sp_https(self, url, xml, metodo, versao_schema=2): finally: certificadoA1.excluir() - def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): + def _post_zeep(self, wsdl, metodo, payload, wcf_compatibility=True): """ - Comunicação wsdl utilizando a biblioteca zeep (GINFES compatível) - - Ex: - _post_zeep( - wsdl, - "ConsultarNfseV3", - xml_consulta ou payload, - ) + Comunicação wsdl utilizando a biblioteca zeep ) + Recebe o wsdl, o método a ser chamado e o payload (XML ou objeto) """ try: import requests @@ -1120,47 +1115,34 @@ def _post_zeep(self, wsdl, metodo, xml, wcf_compatibility=True): from zeep.helpers import serialize_object from zeep.settings import Settings from zeep.transports import Transport - + session = requests.Session() session.verify = False - + # certificado A1 if self.certificado: certificadoA1 = CertificadoA1(self.certificado) chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) session.cert = (cert, chave) - transport = Transport( - session=session, - timeout=60 - ) + transport = Transport(session=session, timeout=60) - settings = Settings( - strict=not wcf_compatibility, - xml_huge_tree=True - ) + settings = Settings(strict=not wcf_compatibility, xml_huge_tree=True) - client = Client( - wsdl=wsdl, - transport=transport, - settings=settings - ) + client = Client(wsdl=wsdl, transport=transport, settings=settings) # DEBUG ÚTIL print("SOAP endpoint:", client.service._binding_options["address"]) - - + if not hasattr(client.service, metodo): raise Exception(f"Método {metodo} não existe no WSDL") - + print("Chamando método:", metodo) - print("Parâmetros:", xml) - print(client.wsdl.services) - print(client.service._binding._operations) - + print("payload:", payload) + service = getattr(client.service, metodo) - response = service(xml) + response = service(payload) return serialize_object(response) finally: From 2e1f5052a0a98eaa5118d432764b90ca7ac1605b Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:18:57 -0300 Subject: [PATCH 105/175] =?UTF-8?q?altera=20importa=C3=A7=C3=A3o=20na=20cl?= =?UTF-8?q?asse=20SerializacaoNfse=20para=20utilizar=20SerializacaoCampina?= =?UTF-8?q?s=20na=20consulta=20de=20faixa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/serializacao.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 756aad25..92622b7e 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2140,9 +2140,9 @@ def __init__(self, autorizador, chave_autenticacao=None): def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): if self.autorizador.lower() == "campinas": - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - return SerializacaoGinfes().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) + return SerializacaoCampinas().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, numero_nota_inicial=numero_inicial, numero_nota_final=numero_final) From dc714313b94b08b7f82bc9f540be3775c5a6c67c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:30:19 -0300 Subject: [PATCH 106/175] =?UTF-8?q?adiciona=20m=C3=A9todos=20de=20consulta?= =?UTF-8?q?=20na=20classe=20SerializacaoCampinas=20e=20ajusta=20chamadas?= =?UTF-8?q?=20na=20classe=20ComunicacaoNfse=20para=20utilizar=20comunica?= =?UTF-8?q?=C3=A7=C3=A3o=20HTTPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 73 ++++++++++++++++--------- pynfe/processamento/comunicacao.py | 45 ++++----------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 49ed69ae..7396526f 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -43,35 +43,56 @@ def consultar( "NumeroReciboUnico": numero_rps_unico, } +from lxml import etree +from pynfe.processamento.autorizador_nfse import InterfaceAutorizador + + class SerializacaoCampinas(InterfaceAutorizador): + """ + Serialização ABRASF v2.03 – Campinas + Retorna XML SEM assinatura (assinatura deve ser aplicada depois). + """ + + NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" + NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - return { - "Prestador": { - "CpfCnpj": { - "Cnpj": emitente.cnpj - }, - "InscricaoMunicipal": emitente.inscricao_municipal - }, - "PeriodoEmissao": { - "DataInicial": data_inicio, - "DataFinal": data_fim - }, - "Pagina": pagina - } + raiz = etree.Element( + "ConsultarNfseServicoPrestadoEnvio", + xmlns=self.NS_PERIODO + ) + + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + + periodo = etree.SubElement(raiz, "PeriodoEmissao") + etree.SubElement(periodo, "DataInicial").text = data_inicio + etree.SubElement(periodo, "DataFinal").text = data_fim + + etree.SubElement(raiz, "Pagina").text = str(pagina) + + return raiz + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - return { - "Prestador": { - "CpfCnpj": { - "Cnpj": emitente.cnpj - }, - "InscricaoMunicipal": emitente.inscricao_municipal - }, - "Faixa": { - "NumeroNfseInicial": numero_inicial, - "NumeroNfseFinal": numero_final - }, - "Pagina": pagina - } + raiz = etree.Element( + "ConsultarNfseFaixaEnvio", + xmlns=self.NS_FAIXA + ) + + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + + faixa = etree.SubElement(raiz, "Faixa") + etree.SubElement(faixa, "NumeroNfseInicial").text = str(numero_inicial) + etree.SubElement(faixa, "NumeroNfseFinal").text = str(numero_final) + + etree.SubElement(raiz, "Pagina").text = str(pagina) + + return raiz class SerializacaoBetha(InterfaceAutorizador): diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index c337306b..e6f5b5dc 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -773,7 +773,7 @@ def consultar_rps(self, payload): from pynfe.processamento.autorizador_nfse import SerializacaoCampinas cabecalho = SerializacaoCampinas().cabecalho() - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, payload) + return self._post_https(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, payload) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -784,7 +784,7 @@ def consultar_faixa(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) + return self._post_https(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -795,10 +795,10 @@ def consultar_periodo(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + return self._post_https(url, NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) elif self.autorizador == "OSASCO": # comunica via wsdl - return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payloadpayload) + return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) else: raise Exception("Este método não esta implementado para o autorizador.") @@ -895,7 +895,7 @@ def _post(self, url, xml, metodo): except Exception as e: raise e - def _post_https(self, url, xml, metodo): + def _post_https(self, url, metodo, xml): """Comunicação wsdl (https) utilizando certificado do usuário""" # cabecalho cabecalho = self._cabecalho() @@ -904,40 +904,15 @@ def _post_https(self, url, xml, metodo): from pynfe.utils.https_nfse import HttpAuthenticated from suds.client import Client - certificadoA1 = CertificadoA1(self.certificado) - chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) + certificado_a1 = CertificadoA1(self.certificado) - wsdl = url # ?wsdl - endpoint = url.replace("?wsdl", "") # REMOVE ?wsdl + chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) - cliente = Client( - wsdl, - transport=HttpAuthenticated( - key=chave, cert=cert, endereco=endpoint # ✅ endpoint correto - ), - ) + cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) # gerar nfse - if metodo == "gerar": - return cliente.service.GerarNfse(cabecalho, xml) - elif metodo == "enviar_lote": - return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) - elif metodo == "consulta": - return cliente.service.ConsultarNfseV3(cabecalho, xml) - elif metodo == "consulta_lote": - return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) - elif metodo == "consulta_situacao_lote": - return cliente.service.ConsultarSituacaoLoteRpsV3(cabecalho, xml) - elif metodo == "consultaRps": - return cliente.service.ConsultarNfsePorRpsV3(cabecalho, xml) - elif metodo == "consultaFaixa": - return cliente.service.ConsultarNfseFaixa(cabecalho, xml) - elif metodo == "cancelar": - # versão 2 - return cliente.service.CancelarNfse(xml) - # versão 3 - # return cliente.service.CancelarNfseV3(cabecalho, xml) - # TODO outros metodos + if metodo == "ConsultarNfseServicoPrestado": + return cliente.service.ConsultarNfseServicoPrestado(cabecalho, xml) else: raise Exception("Método não implementado no autorizador.") except Exception as e: From 341f9b142c3f5e31f8ced787447a1ec64613ba20 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:32:22 -0300 Subject: [PATCH 107/175] =?UTF-8?q?remove=20importa=C3=A7=C3=B5es=20desnec?= =?UTF-8?q?ess=C3=A1rias=20e=20adiciona=20m=C3=A9todo=20para=20gerar=20ID?= =?UTF-8?q?=20nas=20classes=20de=20serializa=C3=A7=C3=A3o=20de=20Campinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 7396526f..bc239db5 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -43,10 +43,6 @@ def consultar( "NumeroReciboUnico": numero_rps_unico, } -from lxml import etree -from pynfe.processamento.autorizador_nfse import InterfaceAutorizador - - class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -56,10 +52,15 @@ class SerializacaoCampinas(InterfaceAutorizador): NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" + def _gerar_id(self, prefixo): + # padrão aceito por Campinas / GINFES + return f"{prefixo}{uuid.uuid4().hex.upper()}" + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element( "ConsultarNfseServicoPrestadoEnvio", - xmlns=self.NS_PERIODO + xmlns=self.NS_PERIODO, + Id=self._gerar_id("CNFSEPERIODO") ) prestador = etree.SubElement(raiz, "Prestador") @@ -78,7 +79,8 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element( "ConsultarNfseFaixaEnvio", - xmlns=self.NS_FAIXA + xmlns=self.NS_FAIXA, + Id=self._gerar_id("CNFSEFAIXA") ) prestador = etree.SubElement(raiz, "Prestador") @@ -94,7 +96,6 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): return raiz - class SerializacaoBetha(InterfaceAutorizador): def __init__(self): # importa From 0c043de964b724a740007b7a6401b89ecddcb73f Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:34:44 -0300 Subject: [PATCH 108/175] =?UTF-8?q?ajusta=20tratamento=20de=20exce=C3=A7?= =?UTF-8?q?=C3=B5es=20na=20classe=20ComunicacaoNfse=20para=20verificar=20a?= =?UTF-8?q?=20disponibilidade=20do=20m=C3=A9todo=20antes=20da=20chamada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index e6f5b5dc..51adf0ea 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -911,10 +911,11 @@ def _post_https(self, url, metodo, xml): cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) # gerar nfse - if metodo == "ConsultarNfseServicoPrestado": - return cliente.service.ConsultarNfseServicoPrestado(cabecalho, xml) - else: - raise Exception("Método não implementado no autorizador.") + try: + service = getattr(cliente.service, metodo) + except AttributeError: + raise ValueError(f"Método '{metodo}' não disponível para {self.autorizador}.") + return service(cabecalho, xml) except Exception as e: raise e From 48c3606351dd6abc619ec55b89bbf4e6ee29a1a3 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:35:49 -0300 Subject: [PATCH 109/175] =?UTF-8?q?ajusta=20chamada=20do=20m=C3=A9todo=20n?= =?UTF-8?q?a=20classe=20ComunicacaoNfse=20para=20remover=20o=20par=C3=A2me?= =?UTF-8?q?tro=20desnecess=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 51adf0ea..06725d2b 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -915,7 +915,7 @@ def _post_https(self, url, metodo, xml): service = getattr(cliente.service, metodo) except AttributeError: raise ValueError(f"Método '{metodo}' não disponível para {self.autorizador}.") - return service(cabecalho, xml) + return service(xml) except Exception as e: raise e From 14b93b0eb19a46a2bc78bc2a66932cc6fb1918c0 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:42:21 -0300 Subject: [PATCH 110/175] =?UTF-8?q?ajusta=20a=20classe=20ComunicacaoNfse?= =?UTF-8?q?=20para=20utilizar=20comunica=C3=A7=C3=A3o=20SOAP=20em=20vez=20?= =?UTF-8?q?de=20HTTPS=20para=20o=20autorizador=20CAMPINAS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 06725d2b..57b99be3 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -770,10 +770,7 @@ def consultar_rps(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - - cabecalho = SerializacaoCampinas().cabecalho() - return self._post_https(url, NFSE[self.autorizador]["CONSULTA_RPS"], cabecalho, payload) + return self._post_soap_raw(url, payload) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -784,7 +781,7 @@ def consultar_faixa(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_https(url, NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) + return self._post_soap_raw(url, payload) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -795,7 +792,7 @@ def consultar_periodo(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_https(url, NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + return self._post_soap_raw(url, payload) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -894,6 +891,14 @@ def _post(self, url, xml, metodo): raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + def _post_soap_raw(self, url, soap_xml): + return requests.post( + url, + data=soap_xml.encode("utf-8"), + cert=(cert, key), + verify=False, + headers={"Content-Type": "text/xml; charset=utf-8"} + ) def _post_https(self, url, metodo, xml): """Comunicação wsdl (https) utilizando certificado do usuário""" From b8c2ed6170f6116a7cdd42156eb27589316cb1fb Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:43:22 -0300 Subject: [PATCH 111/175] =?UTF-8?q?adiciona=20separa=C3=A7=C3=A3o=20de=20a?= =?UTF-8?q?rquivo=20de=20certificado=20na=20classe=20ComunicacaoNfse=20par?= =?UTF-8?q?a=20uso=20em=20requisi=C3=A7=C3=B5es=20SOAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 57b99be3..effc6b78 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -892,6 +892,8 @@ def _post(self, url, xml, metodo): except Exception as e: raise e def _post_soap_raw(self, url, soap_xml): + certificado_a1 = CertificadoA1(self.certificado) + key, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) return requests.post( url, data=soap_xml.encode("utf-8"), From 518140d45cfbbd55261108f74a6187b94718423e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:48:01 -0300 Subject: [PATCH 112/175] =?UTF-8?q?adiciona=20m=C3=A9todos=20para=20gerar?= =?UTF-8?q?=20cabe=C3=A7alho=20e=20envelope=20SOAP=20na=20classe=20Seriali?= =?UTF-8?q?zacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 48 +++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index bc239db5..286cc09c 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -42,20 +42,46 @@ def consultar( "NumeroReciboFinal": numero_rps_final, "NumeroReciboUnico": numero_rps_unico, } - class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas - Retorna XML SEM assinatura (assinatura deve ser aplicada depois). + Retorna SOAP XML (SEM assinatura). + Assinatura e envio ficam fora. """ NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" def _gerar_id(self, prefixo): - # padrão aceito por Campinas / GINFES return f"{prefixo}{uuid.uuid4().hex.upper()}" + def _cabecalho(self): + return """ + + 2.03 + + """.strip() + + def _soap_envelope(self, metodo, xml_envio): + """ + Envolve o XML de envio no SOAP 1.1 correto + """ + return f""" + + + + + {self._cabecalho()} + {xml_envio} + + + + """.strip() + + # ------------------------- + # CONSULTAR POR PERÍODO + # ------------------------- def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element( "ConsultarNfseServicoPrestadoEnvio", @@ -74,8 +100,15 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + xml_envio = etree.tostring(raiz, encoding="utf-8").decode() + return self._soap_envelope( + "ConsultarNfseServicoPrestado", + xml_envio + ) + # ------------------------- + # CONSULTAR POR FAIXA + # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element( "ConsultarNfseFaixaEnvio", @@ -94,8 +127,11 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz - + xml_envio = etree.tostring(raiz, encoding="utf-8").decode() + return self._soap_envelope( + "ConsultarNfseFaixa", + xml_envio + ) class SerializacaoBetha(InterfaceAutorizador): def __init__(self): # importa From 64fdf69ca189c08e438217b542a066c16389b77a Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 00:49:14 -0300 Subject: [PATCH 113/175] =?UTF-8?q?ajusta=20a=20busca=20da=20refer=C3=AAnc?= =?UTF-8?q?ia=20na=20fun=C3=A7=C3=A3o=20assinar=20da=20classe=20Assinatura?= =?UTF-8?q?A1=20para=20utilizar=20find=20em=20vez=20de=20xpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 70b3b856..50c5e263 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -29,7 +29,7 @@ def __init__(self, certificado, senha): def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.xpath('//*[@Id]')[0].attrib['Id'] + reference = xml.find(".//*[@Id]").attrib["Id"] # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) From 866a655f2ae88f1d97931fde4cc4db2e968dd46b Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:05:44 -0300 Subject: [PATCH 114/175] =?UTF-8?q?altera=20m=C3=A9todo=20=5Fsoap=5Fenvelo?= =?UTF-8?q?pe=20para=20soap=5Fenvelope=20na=20classe=20SerializacaoCampina?= =?UTF-8?q?s=20e=20atualiza=20chamadas=20na=20classe=20ComunicacaoNfse=20p?= =?UTF-8?q?ara=20usar=20o=20novo=20m=C3=A9todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 2 +- pynfe/processamento/comunicacao.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 286cc09c..920e22b1 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -62,7 +62,7 @@ def _cabecalho(self): """.strip() - def _soap_envelope(self, metodo, xml_envio): + def soap_envelope(self, metodo, xml_envio): """ Envolve o XML de envio no SOAP 1.1 correto """ diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index effc6b78..57ae2658 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -770,7 +770,9 @@ def consultar_rps(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_soap_raw(url, payload) + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload) + return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -781,7 +783,9 @@ def consultar_faixa(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_soap_raw(url, payload) + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) + return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -792,7 +796,9 @@ def consultar_periodo(self, payload): # url do serviço url = self._get_url() if self.autorizador == "CAMPINAS": - return self._post_soap_raw(url, payload) + from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) From ce9c264fcb92988d4f338b6d4da8fc0158386377 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:06:47 -0300 Subject: [PATCH 115/175] =?UTF-8?q?remove=20chamadas=20ao=20m=C3=A9todo=20?= =?UTF-8?q?=5Fsoap=5Fenvelope=20nas=20fun=C3=A7=C3=B5es=20consultar=5Fperi?= =?UTF-8?q?odo=20e=20consultar=5Ffaixa=20da=20classe=20SerializacaoCampina?= =?UTF-8?q?s,=20retornando=20diretamente=20o=20XML=20gerado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 26 ++++++++++--------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 920e22b1..e5e9999d 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -42,6 +42,8 @@ def consultar( "NumeroReciboFinal": numero_rps_final, "NumeroReciboUnico": numero_rps_unico, } + + class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -86,7 +88,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element( "ConsultarNfseServicoPrestadoEnvio", xmlns=self.NS_PERIODO, - Id=self._gerar_id("CNFSEPERIODO") + Id=self._gerar_id("CNFSEPERIODO"), ) prestador = etree.SubElement(raiz, "Prestador") @@ -101,19 +103,14 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) xml_envio = etree.tostring(raiz, encoding="utf-8").decode() - return self._soap_envelope( - "ConsultarNfseServicoPrestado", - xml_envio - ) + return xml_envio # ------------------------- # CONSULTAR POR FAIXA # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element( - "ConsultarNfseFaixaEnvio", - xmlns=self.NS_FAIXA, - Id=self._gerar_id("CNFSEFAIXA") + "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") ) prestador = etree.SubElement(raiz, "Prestador") @@ -128,10 +125,9 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) xml_envio = etree.tostring(raiz, encoding="utf-8").decode() - return self._soap_envelope( - "ConsultarNfseFaixa", - xml_envio - ) + return xml_envio + + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): # importa @@ -374,11 +370,11 @@ def serializar_lote_sincrono(self, nfse): return gnfse.toxml(encoding="utf-8", element_name="EnviarLoteRpsSincronoEnvio") - + class SerializacaoGinfes(InterfaceAutorizador): def __init__(self): pass - + def consultar_servico_prestado(self, emitente, data_inicio, data_fim, pagina=1): NS = "http://www.ginfes.com.br/servico_consultar_nfse_servico_prestado_envio_v03.xsd" DS = "http://www.w3.org/2000/09/xmldsig#" @@ -448,5 +444,3 @@ def cabecalho(self): versao_dados.text = "3" return etree.tostring(cabecalho, encoding="utf-8", xml_declaration=True) - - From d74fe39c43edfa05f7c01f6d844a7b46571d2fad Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:11:03 -0300 Subject: [PATCH 116/175] =?UTF-8?q?remove=20convers=C3=A3o=20para=20string?= =?UTF-8?q?=20no=20retorno=20das=20fun=C3=A7=C3=B5es=20consultar=5Fperiodo?= =?UTF-8?q?=20e=20consultar=5Ffaixa=20da=20classe=20SerializacaoCampinas,?= =?UTF-8?q?=20retornando=20o=20objeto=20XML=20diretamente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e5e9999d..afd2f541 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -102,8 +102,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - xml_envio = etree.tostring(raiz, encoding="utf-8").decode() - return xml_envio + return raiz # ------------------------- # CONSULTAR POR FAIXA @@ -124,8 +123,7 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - xml_envio = etree.tostring(raiz, encoding="utf-8").decode() - return xml_envio + return raiz class SerializacaoBetha(InterfaceAutorizador): From ca001e08f0154d078e83db0b8629e3d2da7a0910 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:12:42 -0300 Subject: [PATCH 117/175] =?UTF-8?q?altera=20busca=20da=20refer=C3=AAncia?= =?UTF-8?q?=20na=20fun=C3=A7=C3=A3o=20assinar=20da=20classe=20AssinaturaA1?= =?UTF-8?q?=20para=20utilizar=20xpath=20em=20vez=20de=20find?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 50c5e263..70b3b856 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -29,7 +29,7 @@ def __init__(self, certificado, senha): def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.find(".//*[@Id]").attrib["Id"] + reference = xml.xpath('//*[@Id]')[0].attrib['Id'] # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) From f9909129eaecaf660e436d5364fd2f4e7488f8f0 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:15:52 -0300 Subject: [PATCH 118/175] =?UTF-8?q?adiciona=20m=C3=A9todo=20=5Fcorrigir=5F?= =?UTF-8?q?prefixo=5Fds=20na=20classe=20SerializacaoCampinas=20para=20corr?= =?UTF-8?q?igir=20namespaces=20e=20assinar=20XML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index afd2f541..4bc2e088 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -53,6 +53,7 @@ class SerializacaoCampinas(InterfaceAutorizador): NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" + DS_NS = "http://www.w3.org/2000/09/xmldsig#" def _gerar_id(self, prefixo): return f"{prefixo}{uuid.uuid4().hex.upper()}" @@ -63,6 +64,32 @@ def _cabecalho(self): 2.03 """.strip() + + def _corrigir_prefixo_ds(self, xml_str: str) -> str: + parser = etree.XMLParser(remove_blank_text=True) + root = etree.fromstring(xml_str.encode(), parser) + + nsmap = root.nsmap.copy() + if "ds" not in nsmap: + nsmap["ds"] = self.DS_NS + + signature = root.find(f".//{{{self.DS_NS}}}Signature") + if signature is None: + raise ValueError("Signature não encontrada") + + signature.tag = f"{{{self.DS_NS}}}Signature" + for elem in signature.iter(): + if elem.tag.startswith("{"): + ns, local = elem.tag[1:].split("}") + elem.tag = f"{{{ns}}}{local}" + + etree.cleanup_namespaces(root) + + return etree.tostring( + root, + encoding="utf-8", + xml_declaration=False + ).decode() def soap_envelope(self, metodo, xml_envio): """ @@ -75,7 +102,7 @@ def soap_envelope(self, metodo, xml_envio): {self._cabecalho()} - {xml_envio} + {self._corrigir_prefixo_ds(xml_envio)} From 4f748d1bbe3e30ce9207530cd00bf32faadd5c7f Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:24:46 -0300 Subject: [PATCH 119/175] =?UTF-8?q?altera=20m=C3=A9todo=20=5Fcorrigir=5Fpr?= =?UTF-8?q?efixo=5Fds=20para=20=5Fajustar=5Fassinatura=20na=20classe=20Ser?= =?UTF-8?q?ializacaoCampinas,=20ajustando=20a=20assinatura=20XML=20para=20?= =?UTF-8?q?o=20namespace=20correto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 41 ++++++++++++------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 4bc2e088..453cefb6 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -65,32 +65,31 @@ def _cabecalho(self): """.strip() - def _corrigir_prefixo_ds(self, xml_str: str) -> str: - parser = etree.XMLParser(remove_blank_text=True) - root = etree.fromstring(xml_str.encode(), parser) - - nsmap = root.nsmap.copy() - if "ds" not in nsmap: - nsmap["ds"] = self.DS_NS + def _ajustar_assinatura(self, xml_assinado: etree.Element) -> etree.Element: + """ + Move para nfse:Signature + Mantém SignedInfo / Reference / etc em ds + """ + ns = {"ds": self.DS_NS} - signature = root.find(f".//{{{self.DS_NS}}}Signature") + signature = xml_assinado.find(".//ds:Signature", namespaces=ns) if signature is None: - raise ValueError("Signature não encontrada") + raise ValueError("Signature não encontrada no XML assinado") - signature.tag = f"{{{self.DS_NS}}}Signature" - for elem in signature.iter(): - if elem.tag.startswith("{"): - ns, local = elem.tag[1:].split("}") - elem.tag = f"{{{ns}}}{local}" + parent = signature.getparent() + parent.remove(signature) - etree.cleanup_namespaces(root) + nfse_signature = etree.Element( + f"{{{self.NFSE_NS}}}Signature", + nsmap={"ds": self.DS_NS} + ) - return etree.tostring( - root, - encoding="utf-8", - xml_declaration=False - ).decode() + for child in signature: + nfse_signature.append(child) + parent.append(nfse_signature) + return etree.tostring(xml_assinado, encoding="unicode", pretty_print=False) + def soap_envelope(self, metodo, xml_envio): """ Envolve o XML de envio no SOAP 1.1 correto @@ -102,7 +101,7 @@ def soap_envelope(self, metodo, xml_envio): {self._cabecalho()} - {self._corrigir_prefixo_ds(xml_envio)} + {self._ajustar_assinatura(xml_envio)} From a70db63a652211d544d2a4a119137a3c2b3b37f7 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:27:07 -0300 Subject: [PATCH 120/175] adiciona namespace NFSE na classe SerializacaoCampinas e corrige busca da assinatura no XML --- pynfe/processamento/autorizador_nfse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 453cefb6..201cd3af 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -54,6 +54,7 @@ class SerializacaoCampinas(InterfaceAutorizador): NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" DS_NS = "http://www.w3.org/2000/09/xmldsig#" + NFSE_NS = "http://nfse.abrasf.org.br" def _gerar_id(self, prefixo): return f"{prefixo}{uuid.uuid4().hex.upper()}" @@ -72,7 +73,7 @@ def _ajustar_assinatura(self, xml_assinado: etree.Element) -> etree.Element: """ ns = {"ds": self.DS_NS} - signature = xml_assinado.find(".//ds:Signature", namespaces=ns) + signature = xml_assinado.find(".//ds:Signature", ns) if signature is None: raise ValueError("Signature não encontrada no XML assinado") From 7b8c966b5c718827887a063256e8faab7c4349d7 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:28:57 -0300 Subject: [PATCH 121/175] =?UTF-8?q?ajusta=20a=20assinatura=20XML=20na=20cl?= =?UTF-8?q?asse=20SerializacaoCampinas,=20garantindo=20a=20convers=C3=A3o?= =?UTF-8?q?=20de=20string=20para=20Element=20e=20atualizando=20o=20namespa?= =?UTF-8?q?ce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 201cd3af..990a0104 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -71,7 +71,11 @@ def _ajustar_assinatura(self, xml_assinado: etree.Element) -> etree.Element: Move para nfse:Signature Mantém SignedInfo / Reference / etc em ds """ - ns = {"ds": self.DS_NS} + ns = {"ds": "http://www.w3.org/2000/09/xmldsig#"} + + # 🔴 GARANTIA: converter string → Element + if isinstance(xml_assinado, str): + xml_assinado = etree.fromstring(xml_assinado.encode("utf-8")) signature = xml_assinado.find(".//ds:Signature", ns) if signature is None: @@ -82,14 +86,19 @@ def _ajustar_assinatura(self, xml_assinado: etree.Element) -> etree.Element: nfse_signature = etree.Element( f"{{{self.NFSE_NS}}}Signature", - nsmap={"ds": self.DS_NS} + nsmap={"ds": ns["ds"]} ) for child in signature: nfse_signature.append(child) parent.append(nfse_signature) - return etree.tostring(xml_assinado, encoding="unicode", pretty_print=False) + + return etree.tostring( + xml_assinado, + encoding="utf-8", + xml_declaration=False + ).decode() def soap_envelope(self, metodo, xml_envio): """ From a13604bf827306e689fbe7b444bf53c1fa743b19 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:32:50 -0300 Subject: [PATCH 122/175] =?UTF-8?q?adiciona=20impress=C3=A3o=20do=20XML=20?= =?UTF-8?q?gerado=20na=20classe=20ComunicacaoNfse=20para=20depura=C3=A7?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 57ae2658..398a5519 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -785,6 +785,7 @@ def consultar_faixa(self, payload): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) + print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -798,6 +799,7 @@ def consultar_periodo(self, payload): if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl From d0b2474c721b8ea6cd0f6a09d1caabba2576e79f Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:35:13 -0300 Subject: [PATCH 123/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20=5Fajustar=5Fass?= =?UTF-8?q?inatura=20na=20classe=20SerializacaoCampinas=20para=20converter?= =?UTF-8?q?=20a=20assinatura=20XML=20para=20o=20namespace=20NFSE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 45 +++++++++++++++++++------ 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 990a0104..a17dd1b8 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -66,36 +66,59 @@ def _cabecalho(self): """.strip() - def _ajustar_assinatura(self, xml_assinado: etree.Element) -> etree.Element: + def _ajustar_assinatura(self, xml_assinado) -> str: """ - Move para nfse:Signature - Mantém SignedInfo / Reference / etc em ds + Converte: + ... + + Para: + + + + + """ - ns = {"ds": "http://www.w3.org/2000/09/xmldsig#"} - # 🔴 GARANTIA: converter string → Element + NFSE_NS = "http://nfse.abrasf.org.br" + DS_NS = "http://www.w3.org/2000/09/xmldsig#" + + ns = { + "ds": DS_NS, + "nfse": NFSE_NS, + } + + # garante Element if isinstance(xml_assinado, str): - xml_assinado = etree.fromstring(xml_assinado.encode("utf-8")) + root = etree.fromstring(xml_assinado.encode("utf-8")) + else: + root = xml_assinado - signature = xml_assinado.find(".//ds:Signature", ns) + # acha ds:Signature + signature = root.find(".//ds:Signature", namespaces=ns) if signature is None: - raise ValueError("Signature não encontrada no XML assinado") + raise ValueError("ds:Signature não encontrada") parent = signature.getparent() parent.remove(signature) + # cria nfse:Signature EXPLÍCITO nfse_signature = etree.Element( - f"{{{self.NFSE_NS}}}Signature", - nsmap={"ds": ns["ds"]} + f"{{{NFSE_NS}}}Signature", + nsmap={ + "nfse": NFSE_NS, + "ds": DS_NS, + } ) + # move filhos ds:* para dentro for child in signature: nfse_signature.append(child) + # adiciona no MESMO ponto do XML parent.append(nfse_signature) return etree.tostring( - xml_assinado, + root, encoding="utf-8", xml_declaration=False ).decode() From 65679dce5bfdc5c2a06b86345d0293fda5ec178e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 01:39:14 -0300 Subject: [PATCH 124/175] =?UTF-8?q?remove=20m=C3=A9todo=20=5Fajustar=5Fass?= =?UTF-8?q?inatura=20da=20classe=20SerializacaoCampinas=20e=20ajusta=20cha?= =?UTF-8?q?mada=20no=20m=C3=A9todo=20soap=5Fenvelope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 59 +------------------------ 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index a17dd1b8..7f7f825d 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -65,64 +65,7 @@ def _cabecalho(self): 2.03 """.strip() - - def _ajustar_assinatura(self, xml_assinado) -> str: - """ - Converte: - ... - - Para: - - - - - - """ - - NFSE_NS = "http://nfse.abrasf.org.br" - DS_NS = "http://www.w3.org/2000/09/xmldsig#" - - ns = { - "ds": DS_NS, - "nfse": NFSE_NS, - } - - # garante Element - if isinstance(xml_assinado, str): - root = etree.fromstring(xml_assinado.encode("utf-8")) - else: - root = xml_assinado - - # acha ds:Signature - signature = root.find(".//ds:Signature", namespaces=ns) - if signature is None: - raise ValueError("ds:Signature não encontrada") - - parent = signature.getparent() - parent.remove(signature) - - # cria nfse:Signature EXPLÍCITO - nfse_signature = etree.Element( - f"{{{NFSE_NS}}}Signature", - nsmap={ - "nfse": NFSE_NS, - "ds": DS_NS, - } - ) - - # move filhos ds:* para dentro - for child in signature: - nfse_signature.append(child) - - # adiciona no MESMO ponto do XML - parent.append(nfse_signature) - return etree.tostring( - root, - encoding="utf-8", - xml_declaration=False - ).decode() - def soap_envelope(self, metodo, xml_envio): """ Envolve o XML de envio no SOAP 1.1 correto @@ -134,7 +77,7 @@ def soap_envelope(self, metodo, xml_envio): {self._cabecalho()} - {self._ajustar_assinatura(xml_envio)} + {xml_envio} From 028b653ddfea628c763ee6e00638545429d49421 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 10:39:08 -0300 Subject: [PATCH 125/175] =?UTF-8?q?ajusta=20vers=C3=B5es=20das=20depend?= =?UTF-8?q?=C3=AAncias=20lxml,=20signxml=20e=20suds-py3=20no=20setup.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index c9a8171b..73ec0be0 100644 --- a/setup.py +++ b/setup.py @@ -32,9 +32,9 @@ install_requires=[ "pyopenssl>=23.0.0", "requests", - "lxml", - "signxml", - "suds-py3", + "lxml==6.0.2", + "signxml==3.1.0", + "suds-py3==1.4.5.0", "zeep>=4.3.2", ], extras_require={ From 77018b56c124efd51c5f1b31bf34f48285e0cee8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 11:51:11 -0300 Subject: [PATCH 126/175] =?UTF-8?q?adiciona=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20na=20classe=20SerializacaoCampinas=20para=20assinar=20XML=20?= =?UTF-8?q?e=20atualiza=20m=C3=A9todo=20soap=5Fenvelope=20para=20incluir?= =?UTF-8?q?=20assinatura?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 172 +++++++++++++++++++++--- pynfe/processamento/comunicacao.py | 6 +- 2 files changed, 160 insertions(+), 18 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 7f7f825d..1cbe79af 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -65,23 +65,165 @@ def _cabecalho(self): 2.03 """.strip() + def _sign_xml( + self, + xml_element: etree._Element, + certificate_path: str, + certificate_password: str, + ) -> str: + DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" + + C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" + SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" + ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" + + # Garante que existe Id + element_id = xml_element.get("Id") + if not element_id: + raise ValueError("Elemento raiz precisa ter atributo Id para assinatura") + + # Carrega certificado + with open(certificate_path, "rb") as f: + cert_data = f.read() + + password = certificate_password.encode() + private_key, certificate, _ = pkcs12.load_key_and_certificates( + cert_data, password + ) + + if not private_key or not certificate: + raise ValueError("Falha ao carregar certificado") + + cert_b64 = base64.b64encode( + certificate.public_bytes(Encoding.DER) + ).decode() + + # Canonicaliza XML SEM assinatura + xml_c14n = etree.tostring( + xml_element, method="c14n", exclusive=False, with_comments=False + ) + + digest_value = base64.b64encode( + hashlib.sha1(xml_c14n).digest() + ).decode() + + # ---------- SignedInfo ---------- + signed_info = etree.Element( + "SignedInfo", + nsmap={None: DSIG_NS} + ) + + etree.SubElement( + signed_info, + "CanonicalizationMethod", + Algorithm=C14N_ALG + ) + + etree.SubElement( + signed_info, + "SignatureMethod", + Algorithm=SIGNATURE_ALG + ) + + reference = etree.SubElement( + signed_info, + "Reference", + URI=f"#{element_id}" + ) + + transforms = etree.SubElement(reference, "Transforms") + + etree.SubElement( + transforms, + "Transform", + Algorithm=ENVELOPED_ALG + ) + + etree.SubElement( + transforms, + "Transform", + Algorithm=C14N_ALG + ) + + etree.SubElement( + reference, + "DigestMethod", + Algorithm=DIGEST_ALG + ) + + etree.SubElement( + reference, + "DigestValue" + ).text = digest_value + + # Assina SignedInfo + signed_info_c14n = etree.tostring( + signed_info, method="c14n", exclusive=False, with_comments=False + ) + + signature_value = base64.b64encode( + private_key.sign( + signed_info_c14n, + padding.PKCS1v15(), + hashes.SHA1() + ) + ).decode() + + # ---------- Signature ---------- + signature = etree.Element( + "Signature", + nsmap={None: DSIG_NS} + ) + + signature.append(signed_info) + + etree.SubElement( + signature, + "SignatureValue" + ).text = signature_value + + key_info = etree.SubElement(signature, "KeyInfo") + x509_data = etree.SubElement(key_info, "X509Data") + etree.SubElement( + x509_data, + "X509Certificate" + ).text = cert_b64 + + # Insere assinatura no XML + xml_element.append(signature) + + return etree.tostring( + xml_element, encoding="unicode", pretty_print=False + ) + + + def soap_envelope( + self, + metodo, + xml_envio_element, + certificate_path, + certificate_password, + ): + xml_assinado = self._sign_xml( + xml_envio_element, + certificate_path, + certificate_password, + ) - def soap_envelope(self, metodo, xml_envio): - """ - Envolve o XML de envio no SOAP 1.1 correto - """ return f""" - - - - - {self._cabecalho()} - {xml_envio} - - - - """.strip() + + + + + {self._cabecalho()} + {xml_assinado} + + + + """.strip() + # ------------------------- # CONSULTAR POR PERÍODO diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 398a5519..f55d2b99 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -771,7 +771,7 @@ def consultar_rps(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload, self.certificado, self.certificado_senha) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -784,7 +784,7 @@ def consultar_faixa(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload, self.certificado, self.certificado_senha) print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": @@ -798,7 +798,7 @@ def consultar_periodo(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload, self.certificado, self.certificado_senha) print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": From b52687235569c4f3c848e2e073ac466d8c337131 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 11:52:25 -0300 Subject: [PATCH 127/175] =?UTF-8?q?adiciona=20importa=C3=A7=C3=B5es=20de?= =?UTF-8?q?=20base64=20e=20hashlib=20para=20suporte=20a=20opera=C3=A7?= =?UTF-8?q?=C3=B5es=20de=20assinatura?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 1cbe79af..c3dbe833 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -3,6 +3,12 @@ from lxml import etree from pyxb import BIND +import base64 +import hashlib + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.serialization import Encoding, pkcs12 class InterfaceAutorizador: From 6e8a300596b9f9821469ecc94708eee225533bf2 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 11:54:18 -0300 Subject: [PATCH 128/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20=5Fsign=5Fxml=20?= =?UTF-8?q?na=20classe=20SerializacaoCampinas=20para=20normalizar=20entrad?= =?UTF-8?q?a=20e=20organiza=20coment=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 42 +++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index c3dbe833..826814a1 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -71,9 +71,10 @@ def _cabecalho(self): 2.03 """.strip() + def _sign_xml( self, - xml_element: etree._Element, + xml_input, certificate_path: str, certificate_password: str, ) -> str: @@ -84,12 +85,22 @@ def _sign_xml( DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - # Garante que existe Id + # -------------------------------- + # Normaliza entrada (str → Element) + # -------------------------------- + if isinstance(xml_input, str): + parser = etree.XMLParser(remove_blank_text=True) + xml_element = etree.fromstring(xml_input.encode("utf-8"), parser) + else: + xml_element = xml_input + element_id = xml_element.get("Id") if not element_id: raise ValueError("Elemento raiz precisa ter atributo Id para assinatura") + # -------------------------------- # Carrega certificado + # -------------------------------- with open(certificate_path, "rb") as f: cert_data = f.read() @@ -105,7 +116,9 @@ def _sign_xml( certificate.public_bytes(Encoding.DER) ).decode() - # Canonicaliza XML SEM assinatura + # -------------------------------- + # Digest + # -------------------------------- xml_c14n = etree.tostring( xml_element, method="c14n", exclusive=False, with_comments=False ) @@ -114,11 +127,10 @@ def _sign_xml( hashlib.sha1(xml_c14n).digest() ).decode() - # ---------- SignedInfo ---------- - signed_info = etree.Element( - "SignedInfo", - nsmap={None: DSIG_NS} - ) + # -------------------------------- + # SignedInfo + # -------------------------------- + signed_info = etree.Element("SignedInfo", nsmap={None: DSIG_NS}) etree.SubElement( signed_info, @@ -163,7 +175,6 @@ def _sign_xml( "DigestValue" ).text = digest_value - # Assina SignedInfo signed_info_c14n = etree.tostring( signed_info, method="c14n", exclusive=False, with_comments=False ) @@ -176,12 +187,10 @@ def _sign_xml( ) ).decode() - # ---------- Signature ---------- - signature = etree.Element( - "Signature", - nsmap={None: DSIG_NS} - ) - + # -------------------------------- + # Signature + # -------------------------------- + signature = etree.Element("Signature", nsmap={None: DSIG_NS}) signature.append(signed_info) etree.SubElement( @@ -196,7 +205,7 @@ def _sign_xml( "X509Certificate" ).text = cert_b64 - # Insere assinatura no XML + # Insere assinatura xml_element.append(signature) return etree.tostring( @@ -204,6 +213,7 @@ def _sign_xml( ) + def soap_envelope( self, metodo, From 2ac11943af3480e80b8617cb209548e4de36745c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:00:15 -0300 Subject: [PATCH 129/175] =?UTF-8?q?refatora=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20na=20classe=20SerializacaoCampinas=20para=20melhorar=20a=20o?= =?UTF-8?q?rganiza=C3=A7=C3=A3o=20do=20c=C3=B3digo=20e=20adicionar=20comen?= =?UTF-8?q?t=C3=A1rios=20explicativos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 144 ++++++++++++++++-------- 1 file changed, 100 insertions(+), 44 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 826814a1..25db04ea 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -78,16 +78,35 @@ def _sign_xml( certificate_path: str, certificate_password: str, ) -> str: + from lxml import etree + from cryptography.hazmat.primitives.serialization import pkcs12, Encoding + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives import hashes + import base64 + import hashlib + + # ========================= + # Namespaces + # ========================= + NFSE_NS = "http://nfse.abrasf.org.br" DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" + NSMAP = { + "nfse": NFSE_NS, + "xd": DSIG_NS, + } + + # ========================= + # Algoritmos + # ========================= C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - # -------------------------------- - # Normaliza entrada (str → Element) - # -------------------------------- + # ========================= + # Normaliza entrada + # ========================= if isinstance(xml_input, str): parser = etree.XMLParser(remove_blank_text=True) xml_element = etree.fromstring(xml_input.encode("utf-8"), parser) @@ -98,15 +117,15 @@ def _sign_xml( if not element_id: raise ValueError("Elemento raiz precisa ter atributo Id para assinatura") - # -------------------------------- - # Carrega certificado - # -------------------------------- + # ========================= + # Certificado + # ========================= with open(certificate_path, "rb") as f: cert_data = f.read() - password = certificate_password.encode() private_key, certificate, _ = pkcs12.load_key_and_certificates( - cert_data, password + cert_data, + certificate_password.encode(), ) if not private_key or not certificate: @@ -116,100 +135,137 @@ def _sign_xml( certificate.public_bytes(Encoding.DER) ).decode() - # -------------------------------- - # Digest - # -------------------------------- + # ========================= + # Digest do XML (sem assinatura) + # ========================= xml_c14n = etree.tostring( - xml_element, method="c14n", exclusive=False, with_comments=False + xml_element, + method="c14n", + exclusive=False, + with_comments=False, ) digest_value = base64.b64encode( hashlib.sha1(xml_c14n).digest() ).decode() - # -------------------------------- - # SignedInfo - # -------------------------------- - signed_info = etree.Element("SignedInfo", nsmap={None: DSIG_NS}) + # ========================= + # nfse:Signature + # ========================= + signature = etree.Element( + etree.QName(NFSE_NS, "Signature"), + nsmap=NSMAP, + ) + + # ========================= + # xd:SignedInfo + # ========================= + signed_info = etree.SubElement( + signature, + etree.QName(DSIG_NS, "SignedInfo"), + Id=f"SI-{element_id}", + ) etree.SubElement( signed_info, - "CanonicalizationMethod", - Algorithm=C14N_ALG + etree.QName(DSIG_NS, "CanonicalizationMethod"), + Algorithm=C14N_ALG, ) etree.SubElement( signed_info, - "SignatureMethod", - Algorithm=SIGNATURE_ALG + etree.QName(DSIG_NS, "SignatureMethod"), + Algorithm=SIGNATURE_ALG, ) reference = etree.SubElement( signed_info, - "Reference", - URI=f"#{element_id}" + etree.QName(DSIG_NS, "Reference"), + URI=f"#{element_id}", ) - transforms = etree.SubElement(reference, "Transforms") + transforms = etree.SubElement( + reference, + etree.QName(DSIG_NS, "Transforms"), + ) etree.SubElement( transforms, - "Transform", - Algorithm=ENVELOPED_ALG + etree.QName(DSIG_NS, "Transform"), + Algorithm=ENVELOPED_ALG, ) etree.SubElement( transforms, - "Transform", - Algorithm=C14N_ALG + etree.QName(DSIG_NS, "Transform"), + Algorithm=C14N_ALG, ) etree.SubElement( reference, - "DigestMethod", - Algorithm=DIGEST_ALG + etree.QName(DSIG_NS, "DigestMethod"), + Algorithm=DIGEST_ALG, ) etree.SubElement( reference, - "DigestValue" + etree.QName(DSIG_NS, "DigestValue"), ).text = digest_value + # ========================= + # Assina SignedInfo + # ========================= signed_info_c14n = etree.tostring( - signed_info, method="c14n", exclusive=False, with_comments=False + signed_info, + method="c14n", + exclusive=False, + with_comments=False, ) signature_value = base64.b64encode( private_key.sign( signed_info_c14n, padding.PKCS1v15(), - hashes.SHA1() + hashes.SHA1(), ) ).decode() - # -------------------------------- - # Signature - # -------------------------------- - signature = etree.Element("Signature", nsmap={None: DSIG_NS}) - signature.append(signed_info) - + # ========================= + # xd:SignatureValue + # ========================= etree.SubElement( signature, - "SignatureValue" + etree.QName(DSIG_NS, "SignatureValue"), + Id=f"SV-{element_id}", ).text = signature_value - key_info = etree.SubElement(signature, "KeyInfo") - x509_data = etree.SubElement(key_info, "X509Data") + # ========================= + # xd:KeyInfo (EndCertOnly) + # ========================= + key_info = etree.SubElement( + signature, + etree.QName(DSIG_NS, "KeyInfo"), + ) + + x509_data = etree.SubElement( + key_info, + etree.QName(DSIG_NS, "X509Data"), + ) + etree.SubElement( x509_data, - "X509Certificate" + etree.QName(DSIG_NS, "X509Certificate"), ).text = cert_b64 - # Insere assinatura + # ========================= + # Anexa assinatura ao XML + # ========================= xml_element.append(signature) return etree.tostring( - xml_element, encoding="unicode", pretty_print=False + xml_element, + encoding="unicode", + pretty_print=False, ) From 44fc055e63838c4a201bb3ac34c86e88a11e743e Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:06:55 -0300 Subject: [PATCH 130/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20=5Fsign=5Fxml=20?= =?UTF-8?q?na=20classe=20SerializacaoCampinas=20para=20remover=20assinatur?= =?UTF-8?q?as=20existentes=20antes=20de=20calcular=20o=20digest=20do=20XML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 25db04ea..e072db5f 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -136,10 +136,20 @@ def _sign_xml( ).decode() # ========================= - # Digest do XML (sem assinatura) + # Digest SEM a Signature # ========================= + xml_clone = etree.fromstring( + etree.tostring(xml_element) + ) + + # Remove qualquer Signature existente (segurança) + for sig in xml_clone.xpath( + ".//*[local-name()='Signature']" + ): + sig.getparent().remove(sig) + xml_c14n = etree.tostring( - xml_element, + xml_clone, method="c14n", exclusive=False, with_comments=False, From 359cbdaa6daef99d846c69d7c86dcc074bbdc5df Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:18:19 -0300 Subject: [PATCH 131/175] =?UTF-8?q?refatora=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20na=20classe=20SerializacaoCampinas=20para=20melhorar=20a=20l?= =?UTF-8?q?egibilidade=20e=20remover=20coment=C3=A1rios=20desnecess=C3=A1r?= =?UTF-8?q?ios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e072db5f..702130ac 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -3,12 +3,6 @@ from lxml import etree from pyxb import BIND -import base64 -import hashlib - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives.serialization import Encoding, pkcs12 class InterfaceAutorizador: @@ -97,7 +91,7 @@ def _sign_xml( } # ========================= - # Algoritmos + # Algoritmos (GINFES) # ========================= C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" @@ -115,7 +109,7 @@ def _sign_xml( element_id = xml_element.get("Id") if not element_id: - raise ValueError("Elemento raiz precisa ter atributo Id para assinatura") + raise ValueError("Elemento raiz precisa ter atributo Id") # ========================= # Certificado @@ -136,16 +130,14 @@ def _sign_xml( ).decode() # ========================= - # Digest SEM a Signature + # Digest (SEM Signature) # ========================= xml_clone = etree.fromstring( etree.tostring(xml_element) ) - # Remove qualquer Signature existente (segurança) - for sig in xml_clone.xpath( - ".//*[local-name()='Signature']" - ): + # remove qualquer Signature existente + for sig in xml_clone.xpath(".//*[local-name()='Signature']"): sig.getparent().remove(sig) xml_c14n = etree.tostring( @@ -240,9 +232,6 @@ def _sign_xml( ) ).decode() - # ========================= - # xd:SignatureValue - # ========================= etree.SubElement( signature, etree.QName(DSIG_NS, "SignatureValue"), @@ -268,7 +257,7 @@ def _sign_xml( ).text = cert_b64 # ========================= - # Anexa assinatura ao XML + # Anexa assinatura # ========================= xml_element.append(signature) @@ -280,6 +269,7 @@ def _sign_xml( + def soap_envelope( self, metodo, From 68d3a13317389a94f0eb944b4fb2b252c6954a80 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:20:51 -0300 Subject: [PATCH 132/175] =?UTF-8?q?ajusta=20namespace=20da=20assinatura=20?= =?UTF-8?q?no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20SerializacaoCa?= =?UTF-8?q?mpinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 702130ac..b0f9a012 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -155,8 +155,8 @@ def _sign_xml( # nfse:Signature # ========================= signature = etree.Element( - etree.QName(NFSE_NS, "Signature"), - nsmap=NSMAP, + etree.QName(DSIG_NS, "Signature"), + nsmap={None: DSIG_NS}, ) # ========================= From 64cbddbde076dd28dacc5d9fcfd99a455bd1fdc4 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:22:23 -0300 Subject: [PATCH 133/175] =?UTF-8?q?remove=20URI=20parameter=20na=20cria?= =?UTF-8?q?=C3=A7=C3=A3o=20do=20elemento=20Reference=20no=20m=C3=A9todo=20?= =?UTF-8?q?=5Fsign=5Fxml=20da=20classe=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index b0f9a012..605ff4c4 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -183,7 +183,6 @@ def _sign_xml( reference = etree.SubElement( signed_info, etree.QName(DSIG_NS, "Reference"), - URI=f"#{element_id}", ) transforms = etree.SubElement( From 569f8a6f4a74506238e9acb0f84779e48bf59da2 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:25:06 -0300 Subject: [PATCH 134/175] =?UTF-8?q?remove=20par=C3=A2metro=20Id=20na=20cri?= =?UTF-8?q?a=C3=A7=C3=A3o=20do=20elemento=20ConsultarNfseServicoPrestadoEn?= =?UTF-8?q?vio=20e=20ConsultarNfseFaixaEnvio=20na=20classe=20SerializacaoC?= =?UTF-8?q?ampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 605ff4c4..88726acc 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -303,7 +303,6 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element( "ConsultarNfseServicoPrestadoEnvio", xmlns=self.NS_PERIODO, - Id=self._gerar_id("CNFSEPERIODO"), ) prestador = etree.SubElement(raiz, "Prestador") @@ -324,7 +323,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element( - "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") + "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA ) prestador = etree.SubElement(raiz, "Prestador") From c42fc8185df2685b07c05f87df5bdaa0d568dcab Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:28:40 -0300 Subject: [PATCH 135/175] =?UTF-8?q?ajusta=20tratamento=20do=20atributo=20I?= =?UTF-8?q?d=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20Serializac?= =?UTF-8?q?aoCampinas=20para=20permitir=20valores=20nulos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 88726acc..47072b2c 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -108,8 +108,6 @@ def _sign_xml( xml_element = xml_input element_id = xml_element.get("Id") - if not element_id: - raise ValueError("Elemento raiz precisa ter atributo Id") # ========================= # Certificado @@ -165,7 +163,7 @@ def _sign_xml( signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), - Id=f"SI-{element_id}", + Id=f"SI-{element_id}" if element_id else None, ) etree.SubElement( @@ -234,7 +232,7 @@ def _sign_xml( etree.SubElement( signature, etree.QName(DSIG_NS, "SignatureValue"), - Id=f"SV-{element_id}", + Id=f"SV-{element_id}" if element_id else None, ).text = signature_value # ========================= From 96231c8f2f276d6c7e95f8d5bcb0b5eaca7c5390 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:30:08 -0300 Subject: [PATCH 136/175] =?UTF-8?q?ajusta=20tratamento=20do=20atributo=20I?= =?UTF-8?q?d=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20Serializac?= =?UTF-8?q?aoCampinas=20para=20evitar=20valores=20nulos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 47072b2c..fcd5db50 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -163,7 +163,7 @@ def _sign_xml( signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), - Id=f"SI-{element_id}" if element_id else None, + Id=f"SI-{element_id}" if element_id else "", ) etree.SubElement( @@ -232,7 +232,7 @@ def _sign_xml( etree.SubElement( signature, etree.QName(DSIG_NS, "SignatureValue"), - Id=f"SV-{element_id}" if element_id else None, + Id=f"SV-{element_id}" if element_id else "", ).text = signature_value # ========================= From 469d6d4b5fc2db9235644ab74de68c275e08d405 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:37:12 -0300 Subject: [PATCH 137/175] =?UTF-8?q?adiciona=20gera=C3=A7=C3=A3o=20de=20ID?= =?UTF-8?q?=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20Serializaca?= =?UTF-8?q?oCampinas=20para=20tratar=20valores=20nulos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index fcd5db50..dca4444d 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -108,6 +108,8 @@ def _sign_xml( xml_element = xml_input element_id = xml_element.get("Id") + if not element_id: + element_id = self._gerar_id("ID") # ========================= # Certificado @@ -264,9 +266,6 @@ def _sign_xml( pretty_print=False, ) - - - def soap_envelope( self, metodo, From 5c3f4522d993f19b7fe454799927a3a303349ae8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:38:07 -0300 Subject: [PATCH 138/175] =?UTF-8?q?ajusta=20gera=C3=A7=C3=A3o=20de=20IDs?= =?UTF-8?q?=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20Serializaca?= =?UTF-8?q?oCampinas=20para=20garantir=20valores=20consistentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index dca4444d..b701d2d4 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -108,8 +108,6 @@ def _sign_xml( xml_element = xml_input element_id = xml_element.get("Id") - if not element_id: - element_id = self._gerar_id("ID") # ========================= # Certificado @@ -165,7 +163,7 @@ def _sign_xml( signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), - Id=f"SI-{element_id}" if element_id else "", + Id=self._gerar_id("SI"), ) etree.SubElement( @@ -234,7 +232,7 @@ def _sign_xml( etree.SubElement( signature, etree.QName(DSIG_NS, "SignatureValue"), - Id=f"SV-{element_id}" if element_id else "", + Id=self._gerar_id("SV"), ).text = signature_value # ========================= From 0bdf163714e354752694d8ff883886bde0908a8c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:39:56 -0300 Subject: [PATCH 139/175] =?UTF-8?q?remove=20uso=20do=20atributo=20Id=20no?= =?UTF-8?q?=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe=20SerializacaoCamp?= =?UTF-8?q?inas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index b701d2d4..c9920c31 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -107,8 +107,6 @@ def _sign_xml( else: xml_element = xml_input - element_id = xml_element.get("Id") - # ========================= # Certificado # ========================= @@ -163,7 +161,6 @@ def _sign_xml( signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), - Id=self._gerar_id("SI"), ) etree.SubElement( @@ -232,7 +229,6 @@ def _sign_xml( etree.SubElement( signature, etree.QName(DSIG_NS, "SignatureValue"), - Id=self._gerar_id("SV"), ).text = signature_value # ========================= From 043786f2098d6cc9ac634d7e7dd007c24c7e034c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:45:08 -0300 Subject: [PATCH 140/175] =?UTF-8?q?adiciona=20normaliza=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20XML=20para=20GINFES=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da?= =?UTF-8?q?=20classe=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 76 ++++++++++++++----------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index c9920c31..59b28a8c 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -43,7 +43,12 @@ def consultar( "NumeroReciboUnico": numero_rps_unico, } - +def normalize_xml_for_ginfes(elem): + for e in elem.iter(): + if e.text: + e.text = e.text.strip() + if e.tail: + e.tail = None class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -79,37 +84,40 @@ def _sign_xml( import base64 import hashlib - # ========================= + # ===================================================== # Namespaces - # ========================= - NFSE_NS = "http://nfse.abrasf.org.br" + # ===================================================== DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" - NSMAP = { - "nfse": NFSE_NS, - "xd": DSIG_NS, - } - - # ========================= + # ===================================================== # Algoritmos (GINFES) - # ========================= + # ===================================================== C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - # ========================= + # ===================================================== + # Helper: normalização GINFES (OBRIGATÓRIA) + # ===================================================== + def _normalize_xml_ginfes(elem): + for e in elem.iter(): + if e.text is not None: + e.text = e.text.strip() + e.tail = None + + # ===================================================== # Normaliza entrada - # ========================= + # ===================================================== if isinstance(xml_input, str): parser = etree.XMLParser(remove_blank_text=True) xml_element = etree.fromstring(xml_input.encode("utf-8"), parser) else: xml_element = xml_input - # ========================= + # ===================================================== # Certificado - # ========================= + # ===================================================== with open(certificate_path, "rb") as f: cert_data = f.read() @@ -125,9 +133,9 @@ def _sign_xml( certificate.public_bytes(Encoding.DER) ).decode() - # ========================= + # ===================================================== # Digest (SEM Signature) - # ========================= + # ===================================================== xml_clone = etree.fromstring( etree.tostring(xml_element) ) @@ -136,6 +144,9 @@ def _sign_xml( for sig in xml_clone.xpath(".//*[local-name()='Signature']"): sig.getparent().remove(sig) + # 🔥 PASSO CRÍTICO PARA GINFES + _normalize_xml_ginfes(xml_clone) + xml_c14n = etree.tostring( xml_clone, method="c14n", @@ -147,17 +158,17 @@ def _sign_xml( hashlib.sha1(xml_c14n).digest() ).decode() - # ========================= - # nfse:Signature - # ========================= + # ===================================================== + # + # ===================================================== signature = etree.Element( etree.QName(DSIG_NS, "Signature"), nsmap={None: DSIG_NS}, ) - # ========================= - # xd:SignedInfo - # ========================= + # ===================================================== + # + # ===================================================== signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), @@ -208,9 +219,9 @@ def _sign_xml( etree.QName(DSIG_NS, "DigestValue"), ).text = digest_value - # ========================= - # Assina SignedInfo - # ========================= + # ===================================================== + # Assina o SignedInfo + # ===================================================== signed_info_c14n = etree.tostring( signed_info, method="c14n", @@ -231,9 +242,9 @@ def _sign_xml( etree.QName(DSIG_NS, "SignatureValue"), ).text = signature_value - # ========================= - # xd:KeyInfo (EndCertOnly) - # ========================= + # ===================================================== + # + # ===================================================== key_info = etree.SubElement( signature, etree.QName(DSIG_NS, "KeyInfo"), @@ -249,9 +260,9 @@ def _sign_xml( etree.QName(DSIG_NS, "X509Certificate"), ).text = cert_b64 - # ========================= - # Anexa assinatura - # ========================= + # ===================================================== + # Anexa assinatura AO ELEMENTO RAIZ DO ENVIO + # ===================================================== xml_element.append(signature) return etree.tostring( @@ -260,6 +271,7 @@ def _sign_xml( pretty_print=False, ) + def soap_envelope( self, metodo, From a1b1d999abc569eee2acfe4e480fba82a5a152c6 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:50:54 -0300 Subject: [PATCH 141/175] =?UTF-8?q?refatora=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20da=20classe=20SerializacaoCampinas=20para=20melhorar=20a=20l?= =?UTF-8?q?egibilidade=20e=20garantir=20a=20presen=C3=A7a=20do=20atributo?= =?UTF-8?q?=20Id=20no=20elemento=20raiz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 90 ++++++++++++------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 59b28a8c..4a4e9465 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -84,40 +84,40 @@ def _sign_xml( import base64 import hashlib - # ===================================================== + # ========================= # Namespaces - # ===================================================== + # ========================= DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" - # ===================================================== + # ========================= # Algoritmos (GINFES) - # ===================================================== + # ========================= C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - # ===================================================== - # Helper: normalização GINFES (OBRIGATÓRIA) - # ===================================================== - def _normalize_xml_ginfes(elem): - for e in elem.iter(): - if e.text is not None: - e.text = e.text.strip() - e.tail = None - - # ===================================================== - # Normaliza entrada - # ===================================================== + # ========================= + # Parse XML + # ========================= if isinstance(xml_input, str): parser = etree.XMLParser(remove_blank_text=True) xml_element = etree.fromstring(xml_input.encode("utf-8"), parser) else: xml_element = xml_input - # ===================================================== - # Certificado - # ===================================================== + # ========================= + # Id do elemento raiz (OBRIGATÓRIO) + # ========================= + element_id = xml_element.get("Id") + if not element_id: + raise ValueError( + "Elemento raiz não possui atributo Id (obrigatório para assinatura GINFES)" + ) + + # ========================= + # Carrega certificado + # ========================= with open(certificate_path, "rb") as f: cert_data = f.read() @@ -133,20 +133,17 @@ def _normalize_xml_ginfes(elem): certificate.public_bytes(Encoding.DER) ).decode() - # ===================================================== - # Digest (SEM Signature) - # ===================================================== - xml_clone = etree.fromstring( - etree.tostring(xml_element) - ) + # ========================= + # Digest (remove Signature) + # ========================= + xml_clone = etree.fromstring(etree.tostring(xml_element)) - # remove qualquer Signature existente - for sig in xml_clone.xpath(".//*[local-name()='Signature']"): + for sig in xml_clone.xpath( + ".//*[local-name()='Signature' and namespace-uri()=$ns]", + ns=DSIG_NS, + ): sig.getparent().remove(sig) - # 🔥 PASSO CRÍTICO PARA GINFES - _normalize_xml_ginfes(xml_clone) - xml_c14n = etree.tostring( xml_clone, method="c14n", @@ -158,17 +155,17 @@ def _normalize_xml_ginfes(elem): hashlib.sha1(xml_c14n).digest() ).decode() - # ===================================================== - # - # ===================================================== + # ========================= + # + # ========================= signature = etree.Element( etree.QName(DSIG_NS, "Signature"), nsmap={None: DSIG_NS}, ) - # ===================================================== + # ========================= # - # ===================================================== + # ========================= signed_info = etree.SubElement( signature, etree.QName(DSIG_NS, "SignedInfo"), @@ -189,6 +186,7 @@ def _normalize_xml_ginfes(elem): reference = etree.SubElement( signed_info, etree.QName(DSIG_NS, "Reference"), + URI=f"#{element_id}", ) transforms = etree.SubElement( @@ -219,9 +217,9 @@ def _normalize_xml_ginfes(elem): etree.QName(DSIG_NS, "DigestValue"), ).text = digest_value - # ===================================================== - # Assina o SignedInfo - # ===================================================== + # ========================= + # Assina SignedInfo + # ========================= signed_info_c14n = etree.tostring( signed_info, method="c14n", @@ -242,9 +240,9 @@ def _normalize_xml_ginfes(elem): etree.QName(DSIG_NS, "SignatureValue"), ).text = signature_value - # ===================================================== - # - # ===================================================== + # ========================= + # + # ========================= key_info = etree.SubElement( signature, etree.QName(DSIG_NS, "KeyInfo"), @@ -260,9 +258,9 @@ def _normalize_xml_ginfes(elem): etree.QName(DSIG_NS, "X509Certificate"), ).text = cert_b64 - # ===================================================== - # Anexa assinatura AO ELEMENTO RAIZ DO ENVIO - # ===================================================== + # ========================= + # Anexa Signature + # ========================= xml_element.append(signature) return etree.tostring( @@ -271,7 +269,6 @@ def _normalize_xml_ginfes(elem): pretty_print=False, ) - def soap_envelope( self, metodo, @@ -306,6 +303,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element( "ConsultarNfseServicoPrestadoEnvio", xmlns=self.NS_PERIODO, + Id=self._gerar_id("CNFSESP") ) prestador = etree.SubElement(raiz, "Prestador") @@ -326,7 +324,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element( - "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA + "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") ) prestador = etree.SubElement(raiz, "Prestador") From 8a602a01814208d1ae817bb3c1182fbc72c79374 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 12:56:30 -0300 Subject: [PATCH 142/175] =?UTF-8?q?adiciona=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=5F2=20na=20classe=20SerializacaoCampinas=20para=20assinatura?= =?UTF-8?q?=20de=20documentos=20XML=20conforme=20manual=20NFS-e=20de=20S?= =?UTF-8?q?=C3=A3o=20Paulo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 168 +++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 4a4e9465..73815021 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -70,6 +70,172 @@ def _cabecalho(self): 2.03 """.strip() + def _sign_xml_2( + self, + xml_str: str, + certificate_path: str, + certificate_password: str, + ) -> str: + """ + Sign XML document using XML Digital Signature (Enveloped) according to São Paulo NFS-e manual v3.3.4. + + According to section 3.2.3 of the manual: + - Padrão de assinatura: XML Digital Signature, formato Enveloped + - Certificado digital: ICP-Brasil (X509Data) + - Cadeia de Certificação: EndCertOnly (apenas certificado do usuário final) + - Função criptográfica: RSA (rsa-sha1) + - Função message digest: SHA-1 + - Codificação: Base64 + - Transformações: Enveloped e C14N + + Args: + xml_str: XML string to sign (must have a Signature placeholder element) + certificate_path: Path to the PFX/P12 certificate file + certificate_password: Certificate password + + Returns: + Signed XML string + """ + from lxml import etree + from cryptography.hazmat.primitives.serialization import pkcs12, Encoding + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives import hashes + import base64 + import hashlib + C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" + SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" + ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" + DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" + + # Parse the XML + parser = etree.XMLParser(remove_blank_text=True) + root = etree.fromstring(xml_str.encode('utf-8'), parser) + + # Find and remove the Signature placeholder + ns = {'ds': DSIG_NS} + signature_elem = root.find('.//ds:Signature', ns) + + if signature_elem is None: + raise ValueError("XML must contain a ds:Signature placeholder element") + + signature_parent = signature_elem.getparent() + signature_index = list(signature_parent).index(signature_elem) + signature_parent.remove(signature_elem) + + # Load certificate + with open(certificate_path, 'rb') as cert_file: + cert_data = cert_file.read() + + password = certificate_password.encode() if isinstance(certificate_password, str) else certificate_password + private_key, certificate, _ = pkcs12.load_key_and_certificates(cert_data, password) + + if private_key is None or certificate is None: + raise ValueError("Could not load private key or certificate from file") + + # Get certificate in DER format for X509Certificate element + cert_der = certificate.public_bytes(Encoding.DER) + cert_b64 = base64.b64encode(cert_der).decode('ascii') + + # Step 1: Canonicalize the document (without signature) for DigestValue + # Using C14N as per manual: http://www.w3.org/TR/2001/REC-xml-c14n-20010315 + xml_c14n = etree.tostring(root, method='c14n', exclusive=False, with_comments=False) + digest_b64 = base64.b64encode(hashlib.sha1(xml_c14n).digest()).decode('ascii') + + # Step 2: Build SignedInfo as a standalone element with explicit namespace + # CRITICAL: SignedInfo must have the namespace declaration for proper C14N + signed_info = etree.Element( + 'SignedInfo', + nsmap={None: DSIG_NS} # Default namespace, no prefix + ) + + canon_method = etree.SubElement(signed_info, 'CanonicalizationMethod') + canon_method.set('Algorithm', C14N_ALG) + + sig_method = etree.SubElement(signed_info, 'SignatureMethod') + sig_method.set('Algorithm', SIGNATURE_ALG) + + reference = etree.SubElement(signed_info, 'Reference') + reference.set('URI', '') + + transforms = etree.SubElement(reference, 'Transforms') + + transform1 = etree.SubElement(transforms, 'Transform') + transform1.set('Algorithm', ENVELOPED_ALG) + + transform2 = etree.SubElement(transforms, 'Transform') + transform2.set('Algorithm', C14N_ALG) + + digest_method = etree.SubElement(reference, 'DigestMethod') + digest_method.set('Algorithm', DIGEST_ALG) + + digest_value_elem = etree.SubElement(reference, 'DigestValue') + digest_value_elem.text = digest_b64 + + # Step 3: Canonicalize SignedInfo for signing + # The namespace declaration MUST be included in the canonicalized form + signed_info_c14n = etree.tostring(signed_info, method='c14n', exclusive=False, with_comments=False) + + # Step 4: Sign the canonicalized SignedInfo with RSA-SHA1 + from cryptography.hazmat.primitives.asymmetric import rsa + if not isinstance(private_key, rsa.RSAPrivateKey): + raise ValueError("Certificate must contain an RSA private key") + + signature_value_bytes = private_key.sign( + signed_info_c14n, + padding.PKCS1v15(), + hashes.SHA1() + ) + signature_value_b64 = base64.b64encode(signature_value_bytes).decode('ascii') + + # Step 5: Build complete Signature element + new_signature = etree.Element( + 'Signature', + nsmap={None: DSIG_NS} # Default namespace, no prefix + ) + + # Append SignedInfo (recreate without standalone namespace for proper nesting) + new_signed_info = etree.SubElement(new_signature, 'SignedInfo') + + new_canon = etree.SubElement(new_signed_info, 'CanonicalizationMethod') + new_canon.set('Algorithm', C14N_ALG) + + new_sig_method = etree.SubElement(new_signed_info, 'SignatureMethod') + new_sig_method.set('Algorithm', SIGNATURE_ALG) + + new_ref = etree.SubElement(new_signed_info, 'Reference') + new_ref.set('URI', '') + + new_transforms = etree.SubElement(new_ref, 'Transforms') + new_t1 = etree.SubElement(new_transforms, 'Transform') + new_t1.set('Algorithm', ENVELOPED_ALG) + new_t2 = etree.SubElement(new_transforms, 'Transform') + new_t2.set('Algorithm', C14N_ALG) + + new_digest_method = etree.SubElement(new_ref, 'DigestMethod') + new_digest_method.set('Algorithm', DIGEST_ALG) + + new_digest_value = etree.SubElement(new_ref, 'DigestValue') + new_digest_value.text = digest_b64 + + # SignatureValue + sig_value = etree.SubElement(new_signature, 'SignatureValue') + sig_value.text = signature_value_b64 + + # KeyInfo with only X509Certificate (EndCertOnly as per manual) + key_info = etree.SubElement(new_signature, 'KeyInfo') + x509_data = etree.SubElement(key_info, 'X509Data') + x509_cert = etree.SubElement(x509_data, 'X509Certificate') + x509_cert.text = cert_b64 + + # Insert signature into document + signature_parent.insert(signature_index, new_signature) + + # Return signed XML + signed_xml = etree.tostring(root, encoding='unicode', pretty_print=False) + + return signed_xml + def _sign_xml( self, @@ -276,7 +442,7 @@ def soap_envelope( certificate_path, certificate_password, ): - xml_assinado = self._sign_xml( + xml_assinado = self._sign_xml_2( xml_envio_element, certificate_path, certificate_password, From 86c5a2981476fed57bbb92aa7b1ab3e952f3e61a Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 13:02:49 -0300 Subject: [PATCH 143/175] =?UTF-8?q?remove=20m=C3=A9todo=20=5Fsign=5Fxml=5F?= =?UTF-8?q?2=20da=20classe=20SerializacaoCampinas=20e=20ajusta=20implement?= =?UTF-8?q?a=C3=A7=C3=A3o=20de=20=5Fsign=5Fxml=20para=20assinatura=20de=20?= =?UTF-8?q?documentos=20XML=20conforme=20manual=20NFS-e=20de=20Campinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 426 ++++++------------------ 1 file changed, 101 insertions(+), 325 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 73815021..e3f3f94c 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -70,176 +70,10 @@ def _cabecalho(self): 2.03 """.strip() - def _sign_xml_2( - self, - xml_str: str, - certificate_path: str, - certificate_password: str, - ) -> str: - """ - Sign XML document using XML Digital Signature (Enveloped) according to São Paulo NFS-e manual v3.3.4. - - According to section 3.2.3 of the manual: - - Padrão de assinatura: XML Digital Signature, formato Enveloped - - Certificado digital: ICP-Brasil (X509Data) - - Cadeia de Certificação: EndCertOnly (apenas certificado do usuário final) - - Função criptográfica: RSA (rsa-sha1) - - Função message digest: SHA-1 - - Codificação: Base64 - - Transformações: Enveloped e C14N - - Args: - xml_str: XML string to sign (must have a Signature placeholder element) - certificate_path: Path to the PFX/P12 certificate file - certificate_password: Certificate password - - Returns: - Signed XML string - """ - from lxml import etree - from cryptography.hazmat.primitives.serialization import pkcs12, Encoding - from cryptography.hazmat.primitives.asymmetric import padding - from cryptography.hazmat.primitives import hashes - import base64 - import hashlib - C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" - ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" - - # Parse the XML - parser = etree.XMLParser(remove_blank_text=True) - root = etree.fromstring(xml_str.encode('utf-8'), parser) - - # Find and remove the Signature placeholder - ns = {'ds': DSIG_NS} - signature_elem = root.find('.//ds:Signature', ns) - - if signature_elem is None: - raise ValueError("XML must contain a ds:Signature placeholder element") - - signature_parent = signature_elem.getparent() - signature_index = list(signature_parent).index(signature_elem) - signature_parent.remove(signature_elem) - - # Load certificate - with open(certificate_path, 'rb') as cert_file: - cert_data = cert_file.read() - - password = certificate_password.encode() if isinstance(certificate_password, str) else certificate_password - private_key, certificate, _ = pkcs12.load_key_and_certificates(cert_data, password) - - if private_key is None or certificate is None: - raise ValueError("Could not load private key or certificate from file") - - # Get certificate in DER format for X509Certificate element - cert_der = certificate.public_bytes(Encoding.DER) - cert_b64 = base64.b64encode(cert_der).decode('ascii') - - # Step 1: Canonicalize the document (without signature) for DigestValue - # Using C14N as per manual: http://www.w3.org/TR/2001/REC-xml-c14n-20010315 - xml_c14n = etree.tostring(root, method='c14n', exclusive=False, with_comments=False) - digest_b64 = base64.b64encode(hashlib.sha1(xml_c14n).digest()).decode('ascii') - - # Step 2: Build SignedInfo as a standalone element with explicit namespace - # CRITICAL: SignedInfo must have the namespace declaration for proper C14N - signed_info = etree.Element( - 'SignedInfo', - nsmap={None: DSIG_NS} # Default namespace, no prefix - ) - - canon_method = etree.SubElement(signed_info, 'CanonicalizationMethod') - canon_method.set('Algorithm', C14N_ALG) - - sig_method = etree.SubElement(signed_info, 'SignatureMethod') - sig_method.set('Algorithm', SIGNATURE_ALG) - - reference = etree.SubElement(signed_info, 'Reference') - reference.set('URI', '') - - transforms = etree.SubElement(reference, 'Transforms') - - transform1 = etree.SubElement(transforms, 'Transform') - transform1.set('Algorithm', ENVELOPED_ALG) - - transform2 = etree.SubElement(transforms, 'Transform') - transform2.set('Algorithm', C14N_ALG) - - digest_method = etree.SubElement(reference, 'DigestMethod') - digest_method.set('Algorithm', DIGEST_ALG) - - digest_value_elem = etree.SubElement(reference, 'DigestValue') - digest_value_elem.text = digest_b64 - - # Step 3: Canonicalize SignedInfo for signing - # The namespace declaration MUST be included in the canonicalized form - signed_info_c14n = etree.tostring(signed_info, method='c14n', exclusive=False, with_comments=False) - - # Step 4: Sign the canonicalized SignedInfo with RSA-SHA1 - from cryptography.hazmat.primitives.asymmetric import rsa - if not isinstance(private_key, rsa.RSAPrivateKey): - raise ValueError("Certificate must contain an RSA private key") - - signature_value_bytes = private_key.sign( - signed_info_c14n, - padding.PKCS1v15(), - hashes.SHA1() - ) - signature_value_b64 = base64.b64encode(signature_value_bytes).decode('ascii') - - # Step 5: Build complete Signature element - new_signature = etree.Element( - 'Signature', - nsmap={None: DSIG_NS} # Default namespace, no prefix - ) - - # Append SignedInfo (recreate without standalone namespace for proper nesting) - new_signed_info = etree.SubElement(new_signature, 'SignedInfo') - - new_canon = etree.SubElement(new_signed_info, 'CanonicalizationMethod') - new_canon.set('Algorithm', C14N_ALG) - - new_sig_method = etree.SubElement(new_signed_info, 'SignatureMethod') - new_sig_method.set('Algorithm', SIGNATURE_ALG) - - new_ref = etree.SubElement(new_signed_info, 'Reference') - new_ref.set('URI', '') - - new_transforms = etree.SubElement(new_ref, 'Transforms') - new_t1 = etree.SubElement(new_transforms, 'Transform') - new_t1.set('Algorithm', ENVELOPED_ALG) - new_t2 = etree.SubElement(new_transforms, 'Transform') - new_t2.set('Algorithm', C14N_ALG) - - new_digest_method = etree.SubElement(new_ref, 'DigestMethod') - new_digest_method.set('Algorithm', DIGEST_ALG) - - new_digest_value = etree.SubElement(new_ref, 'DigestValue') - new_digest_value.text = digest_b64 - - # SignatureValue - sig_value = etree.SubElement(new_signature, 'SignatureValue') - sig_value.text = signature_value_b64 - - # KeyInfo with only X509Certificate (EndCertOnly as per manual) - key_info = etree.SubElement(new_signature, 'KeyInfo') - x509_data = etree.SubElement(key_info, 'X509Data') - x509_cert = etree.SubElement(x509_data, 'X509Certificate') - x509_cert.text = cert_b64 - - # Insert signature into document - signature_parent.insert(signature_index, new_signature) - - # Return signed XML - signed_xml = etree.tostring(root, encoding='unicode', pretty_print=False) - - return signed_xml - def _sign_xml( self, - xml_input, + xml_input: str, certificate_path: str, certificate_password: str, ) -> str: @@ -250,68 +84,42 @@ def _sign_xml( import base64 import hashlib - # ========================= - # Namespaces - # ========================= - DSIG_NS = "http://www.w3.org/2000/09/xmldsig#" - - # ========================= - # Algoritmos (GINFES) - # ========================= + # Algoritmos exigidos pelo GINFES Campinas C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - SIGNATURE_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + SIGN_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - # ========================= - # Parse XML - # ========================= - if isinstance(xml_input, str): - parser = etree.XMLParser(remove_blank_text=True) - xml_element = etree.fromstring(xml_input.encode("utf-8"), parser) - else: - xml_element = xml_input - - # ========================= - # Id do elemento raiz (OBRIGATÓRIO) - # ========================= - element_id = xml_element.get("Id") - if not element_id: - raise ValueError( - "Elemento raiz não possui atributo Id (obrigatório para assinatura GINFES)" - ) + parser = etree.XMLParser(remove_blank_text=True) + root = etree.fromstring(xml_input.encode("utf-8"), parser) - # ========================= - # Carrega certificado - # ========================= + # === LOCAL CORRETO === + envio = root.xpath("//*[local-name()='ConsultarNfseServicoPrestadoEnvio']") + if not envio: + raise ValueError("ConsultarNfseServicoPrestadoEnvio não encontrado") + + envio = envio[0] + + # Remove qualquer assinatura anterior + for s in envio.xpath("./*[local-name()='Signature']"): + envio.remove(s) + + # === CARREGA CERTIFICADO === with open(certificate_path, "rb") as f: - cert_data = f.read() + pfx = f.read() - private_key, certificate, _ = pkcs12.load_key_and_certificates( - cert_data, + private_key, cert, _ = pkcs12.load_key_and_certificates( + pfx, certificate_password.encode(), ) - if not private_key or not certificate: - raise ValueError("Falha ao carregar certificado") - cert_b64 = base64.b64encode( - certificate.public_bytes(Encoding.DER) + cert.public_bytes(Encoding.DER) ).decode() - # ========================= - # Digest (remove Signature) - # ========================= - xml_clone = etree.fromstring(etree.tostring(xml_element)) - - for sig in xml_clone.xpath( - ".//*[local-name()='Signature' and namespace-uri()=$ns]", - ns=DSIG_NS, - ): - sig.getparent().remove(sig) - + # === DIGEST DO XML SEM SIGNATURE === xml_c14n = etree.tostring( - xml_clone, + envio, method="c14n", exclusive=False, with_comments=False, @@ -321,71 +129,51 @@ def _sign_xml( hashlib.sha1(xml_c14n).digest() ).decode() - # ========================= - # - # ========================= - signature = etree.Element( - etree.QName(DSIG_NS, "Signature"), - nsmap={None: DSIG_NS}, - ) + # === SIGNATURE (SEM NAMESPACE) === + signature = etree.Element("Signature") - # ========================= - # - # ========================= - signed_info = etree.SubElement( - signature, - etree.QName(DSIG_NS, "SignedInfo"), - ) + signed_info = etree.SubElement(signature, "SignedInfo") etree.SubElement( signed_info, - etree.QName(DSIG_NS, "CanonicalizationMethod"), + "CanonicalizationMethod", Algorithm=C14N_ALG, ) etree.SubElement( signed_info, - etree.QName(DSIG_NS, "SignatureMethod"), - Algorithm=SIGNATURE_ALG, + "SignatureMethod", + Algorithm=SIGN_ALG, ) - reference = etree.SubElement( - signed_info, - etree.QName(DSIG_NS, "Reference"), - URI=f"#{element_id}", - ) + reference = etree.SubElement(signed_info, "Reference") - transforms = etree.SubElement( - reference, - etree.QName(DSIG_NS, "Transforms"), - ) + transforms = etree.SubElement(reference, "Transforms") etree.SubElement( transforms, - etree.QName(DSIG_NS, "Transform"), + "Transform", Algorithm=ENVELOPED_ALG, ) etree.SubElement( transforms, - etree.QName(DSIG_NS, "Transform"), + "Transform", Algorithm=C14N_ALG, ) etree.SubElement( reference, - etree.QName(DSIG_NS, "DigestMethod"), + "DigestMethod", Algorithm=DIGEST_ALG, ) etree.SubElement( reference, - etree.QName(DSIG_NS, "DigestValue"), + "DigestValue", ).text = digest_value - # ========================= - # Assina SignedInfo - # ========================= + # === ASSINA SIGNEDINFO === signed_info_c14n = etree.tostring( signed_info, method="c14n", @@ -403,108 +191,96 @@ def _sign_xml( etree.SubElement( signature, - etree.QName(DSIG_NS, "SignatureValue"), + "SignatureValue", ).text = signature_value - # ========================= - # - # ========================= - key_info = etree.SubElement( - signature, - etree.QName(DSIG_NS, "KeyInfo"), - ) - - x509_data = etree.SubElement( - key_info, - etree.QName(DSIG_NS, "X509Data"), - ) - + # === KEYINFO (END CERT ONLY) === + key_info = etree.SubElement(signature, "KeyInfo") + x509_data = etree.SubElement(key_info, "X509Data") etree.SubElement( x509_data, - etree.QName(DSIG_NS, "X509Certificate"), + "X509Certificate", ).text = cert_b64 - # ========================= - # Anexa Signature - # ========================= - xml_element.append(signature) + # === ANEXA NO FINAL DO ENVIO === + envio.append(signature) return etree.tostring( - xml_element, + root, encoding="unicode", pretty_print=False, ) - def soap_envelope( - self, - metodo, - xml_envio_element, - certificate_path, - certificate_password, - ): - xml_assinado = self._sign_xml_2( + def soap_envelope( + self, + metodo, xml_envio_element, certificate_path, certificate_password, - ) + ): + xml_assinado = self._sign_xml( + xml_envio_element, + certificate_path, + certificate_password, + ) - return f""" - - - - - {self._cabecalho()} - {xml_assinado} - - - - """.strip() - - - # ------------------------- - # CONSULTAR POR PERÍODO - # ------------------------- - def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - raiz = etree.Element( - "ConsultarNfseServicoPrestadoEnvio", - xmlns=self.NS_PERIODO, - Id=self._gerar_id("CNFSESP") - ) + return f""" + + + + + {self._cabecalho()} + {xml_assinado} + + + + """.strip() - prestador = etree.SubElement(raiz, "Prestador") - cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") - etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj - etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal - periodo = etree.SubElement(raiz, "PeriodoEmissao") - etree.SubElement(periodo, "DataInicial").text = data_inicio - etree.SubElement(periodo, "DataFinal").text = data_fim + # ------------------------- + # CONSULTAR POR PERÍODO + # ------------------------- + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + raiz = etree.Element( + "ConsultarNfseServicoPrestadoEnvio", + xmlns=self.NS_PERIODO, + Id=self._gerar_id("CNFSESP") + ) - etree.SubElement(raiz, "Pagina").text = str(pagina) + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal - return raiz + periodo = etree.SubElement(raiz, "PeriodoEmissao") + etree.SubElement(periodo, "DataInicial").text = data_inicio + etree.SubElement(periodo, "DataFinal").text = data_fim - # ------------------------- - # CONSULTAR POR FAIXA - # ------------------------- - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - raiz = etree.Element( - "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") - ) + etree.SubElement(raiz, "Pagina").text = str(pagina) + + return raiz + + # ------------------------- + # CONSULTAR POR FAIXA + # ------------------------- + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + raiz = etree.Element( + "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") + ) - prestador = etree.SubElement(raiz, "Prestador") - cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") - etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj - etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal - faixa = etree.SubElement(raiz, "Faixa") - etree.SubElement(faixa, "NumeroNfseInicial").text = str(numero_inicial) - etree.SubElement(faixa, "NumeroNfseFinal").text = str(numero_final) + faixa = etree.SubElement(raiz, "Faixa") + etree.SubElement(faixa, "NumeroNfseInicial").text = str(numero_inicial) + etree.SubElement(faixa, "NumeroNfseFinal").text = str(numero_final) - etree.SubElement(raiz, "Pagina").text = str(pagina) + etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + return raiz class SerializacaoBetha(InterfaceAutorizador): From bbd17cf51d9233d4bbbb112d320c3f2534eef619 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 13:04:27 -0300 Subject: [PATCH 144/175] =?UTF-8?q?refatora=20m=C3=A9todo=20soap=5Fenvelop?= =?UTF-8?q?e=20da=20classe=20SerializacaoCampinas=20para=20melhorar=20a=20?= =?UTF-8?q?legibilidade=20e=20garantir=20a=20correta=20formata=C3=A7=C3=A3?= =?UTF-8?q?o=20do=20XML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 114 ++++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e3f3f94c..0e1d6c4a 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -211,76 +211,76 @@ def _sign_xml( pretty_print=False, ) - def soap_envelope( - self, - metodo, + def soap_envelope( + self, + metodo, + xml_envio_element, + certificate_path, + certificate_password, + ): + xml_assinado = self._sign_xml( xml_envio_element, certificate_path, certificate_password, - ): - xml_assinado = self._sign_xml( - xml_envio_element, - certificate_path, - certificate_password, - ) - - return f""" - - - - - {self._cabecalho()} - {xml_assinado} - - - - """.strip() - + ) - # ------------------------- - # CONSULTAR POR PERÍODO - # ------------------------- - def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - raiz = etree.Element( - "ConsultarNfseServicoPrestadoEnvio", - xmlns=self.NS_PERIODO, - Id=self._gerar_id("CNFSESP") - ) + return f""" + + + + + {self._cabecalho()} + {xml_assinado} + + + + """.strip() + + + # ------------------------- + # CONSULTAR POR PERÍODO + # ------------------------- + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + raiz = etree.Element( + "ConsultarNfseServicoPrestadoEnvio", + xmlns=self.NS_PERIODO, + Id=self._gerar_id("CNFSESP") + ) - prestador = etree.SubElement(raiz, "Prestador") - cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") - etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj - etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal - periodo = etree.SubElement(raiz, "PeriodoEmissao") - etree.SubElement(periodo, "DataInicial").text = data_inicio - etree.SubElement(periodo, "DataFinal").text = data_fim + periodo = etree.SubElement(raiz, "PeriodoEmissao") + etree.SubElement(periodo, "DataInicial").text = data_inicio + etree.SubElement(periodo, "DataFinal").text = data_fim - etree.SubElement(raiz, "Pagina").text = str(pagina) + etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + return raiz - # ------------------------- - # CONSULTAR POR FAIXA - # ------------------------- - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - raiz = etree.Element( - "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") - ) + # ------------------------- + # CONSULTAR POR FAIXA + # ------------------------- + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): + raiz = etree.Element( + "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") + ) - prestador = etree.SubElement(raiz, "Prestador") - cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") - etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj - etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + prestador = etree.SubElement(raiz, "Prestador") + cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") + etree.SubElement(cpf_cnpj, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal - faixa = etree.SubElement(raiz, "Faixa") - etree.SubElement(faixa, "NumeroNfseInicial").text = str(numero_inicial) - etree.SubElement(faixa, "NumeroNfseFinal").text = str(numero_final) + faixa = etree.SubElement(raiz, "Faixa") + etree.SubElement(faixa, "NumeroNfseInicial").text = str(numero_inicial) + etree.SubElement(faixa, "NumeroNfseFinal").text = str(numero_final) - etree.SubElement(raiz, "Pagina").text = str(pagina) + etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + return raiz class SerializacaoBetha(InterfaceAutorizador): From cb1f2751c0647ba5863b2a2da201b103dfbb3427 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 13:07:28 -0300 Subject: [PATCH 145/175] =?UTF-8?q?adiciona=20verifica=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20tipo=20para=20xml=5Finput=20no=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20da=20classe=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 0e1d6c4a..9f58596e 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -91,7 +91,12 @@ def _sign_xml( ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" parser = etree.XMLParser(remove_blank_text=True) - root = etree.fromstring(xml_input.encode("utf-8"), parser) + if isinstance(xml_input, etree._Element): + root = xml_input + elif isinstance(xml_input, (str, bytes)): + root = etree.fromstring(xml_input.encode("utf-8"), parser) + else: + raise TypeError("xml_input deve ser str, bytes ou lxml.etree._Element") # === LOCAL CORRETO === envio = root.xpath("//*[local-name()='ConsultarNfseServicoPrestadoEnvio']") From 729f69dde2de092845efa85e715a534dd5a0dd96 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 17:15:44 -0300 Subject: [PATCH 146/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20assinar=20da=20c?= =?UTF-8?q?lasse=20AssinaturaA1=20para=20converter=20string=20em=20element?= =?UTF-8?q?o=20XML=20e=20trata=20caso=20onde=20n=C3=A3o=20h=C3=A1=20atribu?= =?UTF-8?q?to=20Id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/assinatura.py | 5 ++++- setup.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 70b3b856..fa2c98cc 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -28,8 +28,11 @@ def __init__(self, certificado, senha): self.key, self.cert = CertificadoA1(certificado).separar_arquivo(senha) def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree._Element]: + if isinstance(xml, str): + xml = etree.fromstring(xml.encode('utf-8')) + # busca tag que tem id(reference_uri), logo nao importa se tem namespace - reference = xml.xpath('//*[@Id]')[0].attrib['Id'] + reference = xml.xpath('//*[@Id]')[0].attrib['Id'] if xml.xpath('//*[@Id]') else None # retira acentos xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False)) diff --git a/setup.py b/setup.py index 73ec0be0..c9a8171b 100644 --- a/setup.py +++ b/setup.py @@ -32,9 +32,9 @@ install_requires=[ "pyopenssl>=23.0.0", "requests", - "lxml==6.0.2", - "signxml==3.1.0", - "suds-py3==1.4.5.0", + "lxml", + "signxml", + "suds-py3", "zeep>=4.3.2", ], extras_require={ From 362e3a8c27d1b61fde828d713f7d16f362c68cd8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 22:31:01 -0300 Subject: [PATCH 147/175] =?UTF-8?q?adiciona=20atributo=20Id=20ao=20element?= =?UTF-8?q?o=20Signature=20no=20m=C3=A9todo=20=5Fsign=5Fxml=20da=20classe?= =?UTF-8?q?=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 9f58596e..15a0a347 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -135,7 +135,7 @@ def _sign_xml( ).decode() # === SIGNATURE (SEM NAMESPACE) === - signature = etree.Element("Signature") + signature = etree.Element("Signature", Id=f"Signature-{envio.attrib['Id']}") signed_info = etree.SubElement(signature, "SignedInfo") @@ -171,6 +171,7 @@ def _sign_xml( reference, "DigestMethod", Algorithm=DIGEST_ALG, + URI="#" + envio.attrib["Id"], ) etree.SubElement( From 1a747ae8e20ba140f7885ff9f45c0e3de5c66e44 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 23:49:02 -0300 Subject: [PATCH 148/175] =?UTF-8?q?refatora=20m=C3=A9todo=20=5Fsign=5Fxml?= =?UTF-8?q?=20da=20classe=20SerializacaoCampinas=20para=20melhorar=20a=20l?= =?UTF-8?q?egibilidade=20e=20reorganiza=20importa=C3=A7=C3=B5es;=20remove?= =?UTF-8?q?=20m=C3=A9todo=20=5Fcabecalho=20e=20ajusta=20elementos=20XML=20?= =?UTF-8?q?nas=20fun=C3=A7=C3=B5es=20consultar=5Fperiodo=20e=20consultar?= =?UTF-8?q?=5Ffaixa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 39 ++++++++----------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 15a0a347..ba1a7417 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -43,12 +43,15 @@ def consultar( "NumeroReciboUnico": numero_rps_unico, } + def normalize_xml_for_ginfes(elem): for e in elem.iter(): if e.text: e.text = e.text.strip() if e.tail: e.tail = None + + class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -64,26 +67,20 @@ class SerializacaoCampinas(InterfaceAutorizador): def _gerar_id(self, prefixo): return f"{prefixo}{uuid.uuid4().hex.upper()}" - def _cabecalho(self): - return """ - - 2.03 - - """.strip() - def _sign_xml( self, xml_input: str, certificate_path: str, certificate_password: str, ) -> str: - from lxml import etree - from cryptography.hazmat.primitives.serialization import pkcs12, Encoding - from cryptography.hazmat.primitives.asymmetric import padding - from cryptography.hazmat.primitives import hashes import base64 import hashlib + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives.serialization import Encoding, pkcs12 + from lxml import etree + # Algoritmos exigidos pelo GINFES Campinas C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" SIGN_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" @@ -118,9 +115,7 @@ def _sign_xml( certificate_password.encode(), ) - cert_b64 = base64.b64encode( - cert.public_bytes(Encoding.DER) - ).decode() + cert_b64 = base64.b64encode(cert.public_bytes(Encoding.DER)).decode() # === DIGEST DO XML SEM SIGNATURE === xml_c14n = etree.tostring( @@ -130,9 +125,7 @@ def _sign_xml( with_comments=False, ) - digest_value = base64.b64encode( - hashlib.sha1(xml_c14n).digest() - ).decode() + digest_value = base64.b64encode(hashlib.sha1(xml_c14n).digest()).decode() # === SIGNATURE (SEM NAMESPACE) === signature = etree.Element("Signature", Id=f"Signature-{envio.attrib['Id']}") @@ -236,23 +229,17 @@ def soap_envelope( - {self._cabecalho()} {xml_assinado} """.strip() - # ------------------------- # CONSULTAR POR PERÍODO # ------------------------- def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - raiz = etree.Element( - "ConsultarNfseServicoPrestadoEnvio", - xmlns=self.NS_PERIODO, - Id=self._gerar_id("CNFSESP") - ) + raiz = etree.Element("ConsultarNfseServicoPrestadoEnvio", xmlns=self.NS_PERIODO) prestador = etree.SubElement(raiz, "Prestador") cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") @@ -271,9 +258,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): # CONSULTAR POR FAIXA # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - raiz = etree.Element( - "ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA, Id=self._gerar_id("CNFSEFAIXA") - ) + raiz = etree.Element("ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA) prestador = etree.SubElement(raiz, "Prestador") cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") From c6f5ec717a54ef5b81e76a2c0c502b3c431e5d61 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 30 Dec 2025 23:53:14 -0300 Subject: [PATCH 149/175] =?UTF-8?q?remove=20namespace=20declaration=20nos?= =?UTF-8?q?=20m=C3=A9todos=20consultar=5Fperiodo=20e=20consultar=5Ffaixa?= =?UTF-8?q?=20da=20classe=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index ba1a7417..d7707a76 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -239,7 +239,7 @@ def soap_envelope( # CONSULTAR POR PERÍODO # ------------------------- def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): - raiz = etree.Element("ConsultarNfseServicoPrestadoEnvio", xmlns=self.NS_PERIODO) + raiz = etree.Element("ConsultarNfseServicoPrestadoEnvio") prestador = etree.SubElement(raiz, "Prestador") cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") @@ -258,7 +258,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): # CONSULTAR POR FAIXA # ------------------------- def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): - raiz = etree.Element("ConsultarNfseFaixaEnvio", xmlns=self.NS_FAIXA) + raiz = etree.Element("ConsultarNfseFaixaEnvio") prestador = etree.SubElement(raiz, "Prestador") cpf_cnpj = etree.SubElement(prestador, "CpfCnpj") From c27bb3c1c48f18f66be7844d866faf63bcfe46be Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 31 Dec 2025 09:23:38 -0300 Subject: [PATCH 150/175] =?UTF-8?q?remove=20par=C3=A2metros=20de=20certifi?= =?UTF-8?q?cado=20do=20m=C3=A9todo=20soap=5Fenvelope=20da=20classe=20Seria?= =?UTF-8?q?lizacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 196 +++--------------------- pynfe/processamento/comunicacao.py | 6 +- 2 files changed, 25 insertions(+), 177 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index d7707a76..7c3278c6 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -51,7 +51,6 @@ def normalize_xml_for_ginfes(elem): if e.tail: e.tail = None - class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -59,185 +58,36 @@ class SerializacaoCampinas(InterfaceAutorizador): Assinatura e envio ficam fora. """ - NS_FAIXA = "http://www.ginfes.com.br/servico_consultar_nfse_faixa_envio_v03.xsd" - NS_PERIODO = "http://www.ginfes.com.br/servico_consultar_nfse_servico_envio_v03.xsd" - DS_NS = "http://www.w3.org/2000/09/xmldsig#" - NFSE_NS = "http://nfse.abrasf.org.br" - def _gerar_id(self, prefixo): return f"{prefixo}{uuid.uuid4().hex.upper()}" - def _sign_xml( - self, - xml_input: str, - certificate_path: str, - certificate_password: str, - ) -> str: - import base64 - import hashlib - - from cryptography.hazmat.primitives import hashes - from cryptography.hazmat.primitives.asymmetric import padding - from cryptography.hazmat.primitives.serialization import Encoding, pkcs12 - from lxml import etree - - # Algoritmos exigidos pelo GINFES Campinas - C14N_ALG = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - SIGN_ALG = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - DIGEST_ALG = "http://www.w3.org/2000/09/xmldsig#sha1" - ENVELOPED_ALG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" - - parser = etree.XMLParser(remove_blank_text=True) - if isinstance(xml_input, etree._Element): - root = xml_input - elif isinstance(xml_input, (str, bytes)): - root = etree.fromstring(xml_input.encode("utf-8"), parser) - else: - raise TypeError("xml_input deve ser str, bytes ou lxml.etree._Element") - - # === LOCAL CORRETO === - envio = root.xpath("//*[local-name()='ConsultarNfseServicoPrestadoEnvio']") - if not envio: - raise ValueError("ConsultarNfseServicoPrestadoEnvio não encontrado") - - envio = envio[0] - - # Remove qualquer assinatura anterior - for s in envio.xpath("./*[local-name()='Signature']"): - envio.remove(s) - - # === CARREGA CERTIFICADO === - with open(certificate_path, "rb") as f: - pfx = f.read() - - private_key, cert, _ = pkcs12.load_key_and_certificates( - pfx, - certificate_password.encode(), - ) - - cert_b64 = base64.b64encode(cert.public_bytes(Encoding.DER)).decode() - - # === DIGEST DO XML SEM SIGNATURE === - xml_c14n = etree.tostring( - envio, - method="c14n", - exclusive=False, - with_comments=False, - ) - - digest_value = base64.b64encode(hashlib.sha1(xml_c14n).digest()).decode() - - # === SIGNATURE (SEM NAMESPACE) === - signature = etree.Element("Signature", Id=f"Signature-{envio.attrib['Id']}") - - signed_info = etree.SubElement(signature, "SignedInfo") - - etree.SubElement( - signed_info, - "CanonicalizationMethod", - Algorithm=C14N_ALG, - ) - - etree.SubElement( - signed_info, - "SignatureMethod", - Algorithm=SIGN_ALG, - ) - - reference = etree.SubElement(signed_info, "Reference") - - transforms = etree.SubElement(reference, "Transforms") - - etree.SubElement( - transforms, - "Transform", - Algorithm=ENVELOPED_ALG, - ) - - etree.SubElement( - transforms, - "Transform", - Algorithm=C14N_ALG, - ) - - etree.SubElement( - reference, - "DigestMethod", - Algorithm=DIGEST_ALG, - URI="#" + envio.attrib["Id"], - ) - - etree.SubElement( - reference, - "DigestValue", - ).text = digest_value - - # === ASSINA SIGNEDINFO === - signed_info_c14n = etree.tostring( - signed_info, - method="c14n", - exclusive=False, - with_comments=False, - ) - - signature_value = base64.b64encode( - private_key.sign( - signed_info_c14n, - padding.PKCS1v15(), - hashes.SHA1(), - ) - ).decode() - - etree.SubElement( - signature, - "SignatureValue", - ).text = signature_value - - # === KEYINFO (END CERT ONLY) === - key_info = etree.SubElement(signature, "KeyInfo") - x509_data = etree.SubElement(key_info, "X509Data") - etree.SubElement( - x509_data, - "X509Certificate", - ).text = cert_b64 - - # === ANEXA NO FINAL DO ENVIO === - envio.append(signature) - - return etree.tostring( - root, - encoding="unicode", - pretty_print=False, - ) - def soap_envelope( self, metodo, - xml_envio_element, - certificate_path, - certificate_password, + xml_assinado, ): - xml_assinado = self._sign_xml( - xml_envio_element, - certificate_path, - certificate_password, + NAMESPACE_SOAP = "http://schemas.xmlsoap.org/soap/envelope/" + NAMESPACE_XSI = "http://www.w3.org/2001/XMLSchema-instance" + NAMESPACE_XSD = "http://www.w3.org/2001/XMLSchema" + NAMESPACE_ABRASF = "http://nfse.abrasf.org.br" + + xml_metodo = etree.Element("{%s}" % NAMESPACE_ABRASF + metodo) + xml_metodo.append(xml_assinado) + raiz = etree.Element( + "{%s}Envelope" % NAMESPACE_SOAP, + nsmap={ + "xsi": NAMESPACE_XSI, + "xsd": NAMESPACE_XSD, + "soap": NAMESPACE_SOAP, + "nfse": NAMESPACE_ABRASF, + }, ) + body = etree.SubElement(raiz, "{%s}Body" % NAMESPACE_SOAP) + body.append(xml_metodo) + + return etree.tostring(raiz, pretty_print=True).decode() + - return f""" - - - - - {xml_assinado} - - - - """.strip() - - # ------------------------- - # CONSULTAR POR PERÍODO - # ------------------------- def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element("ConsultarNfseServicoPrestadoEnvio") @@ -254,9 +104,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): return raiz - # ------------------------- - # CONSULTAR POR FAIXA - # ------------------------- + def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element("ConsultarNfseFaixaEnvio") diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index f55d2b99..398a5519 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -771,7 +771,7 @@ def consultar_rps(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload, self.certificado, self.certificado_senha) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -784,7 +784,7 @@ def consultar_faixa(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload, self.certificado, self.certificado_senha) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": @@ -798,7 +798,7 @@ def consultar_periodo(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload, self.certificado, self.certificado_senha) + soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) print(soup_xml) return self._post_soap_raw(url, soup_xml) elif self.autorizador == "OSASCO": From 2afbffed207a8adb08d395aa462805fd6ab31ebd Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 31 Dec 2025 09:30:34 -0300 Subject: [PATCH 151/175] =?UTF-8?q?ajusta=20m=C3=A9todos=20da=20classe=20C?= =?UTF-8?q?omunicacaoNfse=20para=20assinar=20payloads=20antes=20de=20envia?= =?UTF-8?q?r=20e=20melhora=20a=20formata=C3=A7=C3=A3o=20do=20XML=20retorna?= =?UTF-8?q?do=20na=20classe=20SerializacaoCampinas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 ++-- pynfe/processamento/comunicacao.py | 23 +++++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 7c3278c6..2fe38110 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -102,7 +102,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + return etree.tostring(raiz, pretty_print=True).decode() def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): @@ -119,7 +119,7 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): etree.SubElement(raiz, "Pagina").text = str(pagina) - return raiz + return etree.tostring(raiz, pretty_print=True).decode() class SerializacaoBetha(InterfaceAutorizador): diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 398a5519..46362c59 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -771,8 +771,9 @@ def consultar_rps(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], payload) - return self._post_soap_raw(url, soup_xml) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) + envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], xml_assinado) + return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -784,9 +785,9 @@ def consultar_faixa(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], payload) - print(soup_xml) - return self._post_soap_raw(url, soup_xml) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) + envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], xml_assinado) + return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -798,9 +799,9 @@ def consultar_periodo(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - soup_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) - print(soup_xml) - return self._post_soap_raw(url, soup_xml) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) + envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado) + return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) @@ -856,12 +857,6 @@ def _cabecalho2(self, retorna_string=True): else: return raiz - def _cabecalho_ginfes(self): - """Retorna o XML do cabeçalho gerado pelo xsd""" - from pynfe.processamento.autorizador_nfse import SerializacaoGinfes - - return SerializacaoGinfes().cabecalho() - def _get_url(self): """Retorna a url para comunicação com o webservice""" if self._ambiente == 1: From b7a7ebcd33f7d6f832e14a13c6c3588d4974a1bd Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 31 Dec 2025 10:21:28 -0300 Subject: [PATCH 152/175] =?UTF-8?q?adiciona=20verifica=C3=A7=C3=A3o=20para?= =?UTF-8?q?=20garantir=20que=20o=20certificado=20seja=20exclu=C3=ADdo=20ap?= =?UTF-8?q?enas=20se=20estiver=20presente=20na=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 46362c59..90061f71 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -1132,7 +1132,8 @@ def _post_zeep(self, wsdl, metodo, payload, wcf_compatibility=True): return serialize_object(response) finally: - certificadoA1.excluir() + if self.certificado: + certificadoA1.excluir() class ComunicacaoMDFe(Comunicacao): From 695143569ffb237d7f54c89b68861afec2d5cc04 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Wed, 31 Dec 2025 10:31:43 -0300 Subject: [PATCH 153/175] =?UTF-8?q?remove=20par=C3=A2metro=20cnpj=5Ftomado?= =?UTF-8?q?r=20das=20chamadas=20ao=20m=C3=A9todo=20consultar=20na=20classe?= =?UTF-8?q?=20SerializacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/serializacao.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 92622b7e..760186a4 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2145,7 +2145,7 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): return SerializacaoCampinas().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco - return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, numero_nota_inicial=numero_inicial, numero_nota_final=numero_final) + return SerializacaoOsasco(self.chave_autenticacao).consultar(numero_nota_inicial=numero_inicial, numero_nota_final=numero_final) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") @@ -2157,7 +2157,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco - return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, data_inicial=data_inicio, data_final=data_fim) + return SerializacaoOsasco(self.chave_autenticacao).consultar(data_inicial=data_inicio, data_final=data_fim) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") @@ -2165,7 +2165,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): def consultar_nfse(self, emitente, numero_nfse): if self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco - return SerializacaoOsasco(self.chave_autenticacao).consultar(cnpj_tomador=emitente.cnpj, numero_nota_inicial=numero_nfse, numero_nota_final=numero_nfse) + return SerializacaoOsasco(self.chave_autenticacao).consultar(numero_nota_inicial=numero_nfse, numero_nota_final=numero_nfse) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") From 998a352376b8a9e089ceec05608641575cd42986 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 2 Jan 2026 16:56:28 -0300 Subject: [PATCH 154/175] =?UTF-8?q?adiciona=20suporte=20=C3=A0=20serializa?= =?UTF-8?q?=C3=A7=C3=A3o=20para=20Maracana=C3=BA=20e=20integra=20ao=20flux?= =?UTF-8?q?o=20de=20comunica=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 65 ++++++++++++++++++++++--- pynfe/processamento/comunicacao.py | 4 ++ pynfe/utils/webservices.py | 5 ++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 2fe38110..e1077677 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -44,13 +44,6 @@ def consultar( } -def normalize_xml_for_ginfes(elem): - for e in elem.iter(): - if e.text: - e.text = e.text.strip() - if e.tail: - e.tail = None - class SerializacaoCampinas(InterfaceAutorizador): """ Serialização ABRASF v2.03 – Campinas @@ -121,6 +114,64 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): return etree.tostring(raiz, pretty_print=True).decode() +class SerializacaoMaracanau(InterfaceAutorizador): + """ + Serialização ABRASF v1.00 – Maracanaú + """ + def _cabecalho(self): + cabecalho_xml = """1""".strip() + return cabecalho_xml + + def soap_envelope( + self, + metodo, + xml_envio, + ): + NAMESPACE_SOAP = "http://schemas.xmlsoap.org/soap/envelope/" + NAMESPACE_ABRASF = "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" + + ns_metodo = { + "ConsultarNfse": "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd", + } + + envelope = etree.Element( + "{http://schemas.xmlsoap.org/soap/envelope/}Envelope", + nsmap={ + "soapenv": NAMESPACE_SOAP, + "nfse": NAMESPACE_ABRASF, + "consult": ns_metodo[metodo], + }, + ) + etree.SubElement(envelope, "{http://schemas.xmlsoap.org/soap/envelope/}Header") + body = etree.SubElement(envelope, "{http://schemas.xmlsoap.org/soap/envelope/}Body") + + consultar_nfse = etree.SubElement( + body, + "{%s}" % ns_metodo[metodo] + metodo + ) + + header = etree.SubElement(consultar_nfse, "header") + header.text = etree.CDATA(self._cabecalho()) + + parameters = etree.SubElement(consultar_nfse, "parameters") + parameters.text = etree.CDATA(xml_envio) + + return etree.tostring(envelope, pretty_print=True).decode() + + + def consultar_periodo(self, emitente, data_inicio, data_fim): + raiz = etree.Element("ConsultarNfse") + + prestador = etree.SubElement(raiz, "Prestador") + etree.SubElement(prestador, "Cnpj").text = emitente.cnpj + etree.SubElement(prestador, "InscricaoMunicipal").text = emitente.inscricao_municipal + + periodo = etree.SubElement(raiz, "PeriodoEmissao") + etree.SubElement(periodo, "DataInicial").text = data_inicio + etree.SubElement(periodo, "DataFinal").text = data_fim + + return etree.tostring(raiz, pretty_print=True).decode() + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 90061f71..4ba1b386 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -802,6 +802,10 @@ def consultar_periodo(self, payload): xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado) return self._post_soap_raw(url, envelope_xml) + elif self.autorizador == "MARACANAU": + from pynfe.processamento.autorizador_nfse import SerializacaoMaracanau + envelope_xml = SerializacaoMaracanau().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index a97cada4..0336633e 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -529,6 +529,11 @@ "HTTPS": "https://novanfse.campinas.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", "HOMOLOGACAO": "https://homol-rps.ima.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", }, + "MARACANAU": { + "CONSULTA_SERVICO": "ConsultarNfse", + "HTTPS": "https://speedgov.com.br/wsmar/Nfes?wsdl", + "HOMOLOGACAO": "", + }, } # MDF-e From 6534b1add3b306faff9d037db9b2a30cf79493bc Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 2 Jan 2026 17:02:51 -0300 Subject: [PATCH 155/175] =?UTF-8?q?adiciona=20suporte=20=C3=A0=20serializa?= =?UTF-8?q?=C3=A7=C3=A3o=20para=20Maracana=C3=BA=20na=20classe=20Comunicac?= =?UTF-8?q?aoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 4ba1b386..4aa91c61 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -753,6 +753,9 @@ def __init__(self, autorizador, certificado=None, certificado_senha=None, homolo elif self.autorizador == "CAMPINAS": self._namespace = "http://www.abrasf.org.br/nfse.xsd" self._versao = "2" + elif self.autorizador == "MARACANAU": + self._namespace = "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" + self._versao = "1.00" else: raise Exception("Autorizador não encontrado!") From 15260e18dea31afdd2c41c19b31a66db80c8d348 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 2 Jan 2026 17:09:41 -0300 Subject: [PATCH 156/175] ajusta namespaces e estrutura do XML na classe SerializacaoMaracanau --- pynfe/processamento/autorizador_nfse.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e1077677..9e27a885 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -63,7 +63,7 @@ def soap_envelope( NAMESPACE_XSI = "http://www.w3.org/2001/XMLSchema-instance" NAMESPACE_XSD = "http://www.w3.org/2001/XMLSchema" NAMESPACE_ABRASF = "http://nfse.abrasf.org.br" - + xml_metodo = etree.Element("{%s}" % NAMESPACE_ABRASF + metodo) xml_metodo.append(xml_assinado) raiz = etree.Element( @@ -80,7 +80,6 @@ def soap_envelope( return etree.tostring(raiz, pretty_print=True).decode() - def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): raiz = etree.Element("ConsultarNfseServicoPrestadoEnvio") @@ -97,7 +96,6 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): return etree.tostring(raiz, pretty_print=True).decode() - def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): raiz = etree.Element("ConsultarNfseFaixaEnvio") @@ -114,10 +112,12 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): return etree.tostring(raiz, pretty_print=True).decode() + class SerializacaoMaracanau(InterfaceAutorizador): """ Serialização ABRASF v1.00 – Maracanaú """ + def _cabecalho(self): cabecalho_xml = """1""".strip() return cabecalho_xml @@ -129,9 +129,9 @@ def soap_envelope( ): NAMESPACE_SOAP = "http://schemas.xmlsoap.org/soap/envelope/" NAMESPACE_ABRASF = "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" - + ns_metodo = { - "ConsultarNfse": "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd", + "ConsultarNfse": "http://ws.speedgov.com.br/consultar_nfse_envio_v1.xsd", } envelope = etree.Element( @@ -145,10 +145,7 @@ def soap_envelope( etree.SubElement(envelope, "{http://schemas.xmlsoap.org/soap/envelope/}Header") body = etree.SubElement(envelope, "{http://schemas.xmlsoap.org/soap/envelope/}Body") - consultar_nfse = etree.SubElement( - body, - "{%s}" % ns_metodo[metodo] + metodo - ) + consultar_nfse = etree.SubElement(body, "{%s}" % NAMESPACE_ABRASF + metodo) header = etree.SubElement(consultar_nfse, "header") header.text = etree.CDATA(self._cabecalho()) @@ -158,9 +155,13 @@ def soap_envelope( return etree.tostring(envelope, pretty_print=True).decode() - def consultar_periodo(self, emitente, data_inicio, data_fim): - raiz = etree.Element("ConsultarNfse") + NAMESPACE_SPEEDGOV_CONSULTAR_NFSE_ENVIO = ( + "http://ws.speedgov.com.br/consultar_nfse_envio_v1.xsd" + ) + raiz = etree.Element( + "{%s}ConsultarNfseEnvio" % NAMESPACE_SPEEDGOV_CONSULTAR_NFSE_ENVIO, + ) prestador = etree.SubElement(raiz, "Prestador") etree.SubElement(prestador, "Cnpj").text = emitente.cnpj From 467879d3dfebc0dad8ed6c089d49174c61ccd663 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 10:31:23 -0300 Subject: [PATCH 157/175] =?UTF-8?q?adiciona=20suporte=20=C3=A0=20serializa?= =?UTF-8?q?=C3=A7=C3=A3o=20para=20GISS=20na=20classe=20SerializacaoGiss=20?= =?UTF-8?q?e=20integra=20ao=20fluxo=20de=20comunica=C3=A7=C3=A3o=20na=20cl?= =?UTF-8?q?asse=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 72 +++++++++++++++++++++++++ pynfe/processamento/comunicacao.py | 56 ++++++++++++++----- pynfe/utils/webservices.py | 5 ++ 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index 9e27a885..e31faf67 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -174,6 +174,78 @@ def consultar_periodo(self, emitente, data_inicio, data_fim): return etree.tostring(raiz, pretty_print=True).decode() +class SerializacaoGiss(InterfaceAutorizador): + """ + Serialização ABRASF v2.04 – GISS + """ + + NAMESPACE_SOAP = "http://schemas.xmlsoap.org/soap/envelope/" + NAMESPACE_XSI = "http://www.w3.org/2001/XMLSchema-instance" + NAMESPACE_XSD = "http://www.w3.org/2001/XMLSchema" + NAMESPACE_ABRASF = "http://nfse.abrasf.org.br" + NAMESPACE_METODO = "http://www.giss.com.br/consultar-nfse-servico-prestado-envio-v2_04.xsd" + NAMESPACE_TIPOS = "http://www.giss.com.br/tipos-v2_04.xsd" + NAMESPACE_CABECALHO = "http://www.giss.com.br/cabecalho-v2_04.xsd" + + def _cabecalho(self): + cabecalho_xml = f"""2.04""".strip() + return cabecalho_xml + + def soap_envelope( + self, + metodo, + xml_assinado, + ): + NAMESPACE_SOAP = "http://schemas.xmlsoap.org/soap/envelope/" + NAMESPACE_XSI = "http://www.w3.org/2001/XMLSchema-instance" + NAMESPACE_XSD = "http://www.w3.org/2001/XMLSchema" + NAMESPACE_ABRASF = "http://nfse.abrasf.org.br" + + envelope = etree.Element( + f"{{{self.NAMESPACE_SOAP}}}Envelope", + nsmap={ + "xsi": NAMESPACE_XSI, + "xsd": NAMESPACE_XSD, + "soap": NAMESPACE_SOAP, + "nfse": NAMESPACE_ABRASF, + }, + ) + body = etree.SubElement(envelope, f"{{{self.NAMESPACE_SOAP}}}Body") + + consultar_nfse = etree.SubElement( + body, f"{{{self.NAMESPACE_ABRASF}}}{metodo}Request" + ) + + header = etree.SubElement(consultar_nfse, "nfseCabecMsg") + header.text = etree.CDATA(self._cabecalho()) + + parameters = etree.SubElement(consultar_nfse, "nfseDadosMsg") + parameters.text = etree.CDATA(xml_assinado) + + return ( + '' + + etree.tostring(envelope, pretty_print=True).decode() + ) + + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): + return f""" + + + + {emitente.cnpj} + + {emitente.inscricao_municipal} + + + {data_inicio} + {data_fim} + + {pagina} + """.strip() + + class SerializacaoBetha(InterfaceAutorizador): def __init__(self): # importa diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 4aa91c61..a6f143c3 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -5,7 +5,7 @@ import requests from pynfe.entidades.certificado import CertificadoA1 -from pynfe.utils import etree, so_numeros +from pynfe.utils import etree, obter_municipio_por_codigo, so_numeros from pynfe.utils.flags import ( CODIGOS_ESTADOS, MODELO_MDFE, @@ -737,7 +737,9 @@ class ComunicacaoNfse(Comunicacao): _versao = "" _namespace = "" - def __init__(self, autorizador, certificado=None, certificado_senha=None, homologacao=False): + def __init__( + self, autorizador, certificado=None, certificado_senha=None, homologacao=False, **kwargs + ): self.certificado = certificado self.certificado_senha = certificado_senha self._ambiente = 2 if homologacao else 1 @@ -748,14 +750,17 @@ def __init__(self, autorizador, certificado=None, certificado_senha=None, homolo elif self.autorizador == "BARUERI": self._namespace = "http://www.barueri.sp.gov.br/nfeservice" elif self.autorizador == "OSASCO": - self._namespace = "" - self._versao = "1" + pass elif self.autorizador == "CAMPINAS": - self._namespace = "http://www.abrasf.org.br/nfse.xsd" - self._versao = "2" + pass elif self.autorizador == "MARACANAU": - self._namespace = "http://www.abrasf.org.br/ABRASF/arquivos/nfse.xsd" - self._versao = "1.00" + pass + elif self.autorizador == "GISS": + if not kwargs.get("municipio_codigo"): + raise Exception( + "Para o autorizador GISS é necessário informar o código do município." + ) + self.municipio_codigo = kwargs.get("municipio_codigo") else: raise Exception("Autorizador não encontrado!") @@ -774,8 +779,11 @@ def consultar_rps(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) - envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_RPS"], xml_assinado) + envelope_xml = SerializacaoCampinas().soap_envelope( + NFSE[self.autorizador]["CONSULTA_RPS"], xml_assinado + ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -788,8 +796,11 @@ def consultar_faixa(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) - envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_FAIXA"], xml_assinado) + envelope_xml = SerializacaoCampinas().soap_envelope( + NFSE[self.autorizador]["CONSULTA_FAIXA"], xml_assinado + ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -802,12 +813,25 @@ def consultar_periodo(self, payload): url = self._get_url() if self.autorizador == "CAMPINAS": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) - envelope_xml = SerializacaoCampinas().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado) + envelope_xml = SerializacaoCampinas().soap_envelope( + NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado + ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "MARACANAU": from pynfe.processamento.autorizador_nfse import SerializacaoMaracanau - envelope_xml = SerializacaoMaracanau().soap_envelope(NFSE[self.autorizador]["CONSULTA_SERVICO"], payload) + + envelope_xml = SerializacaoMaracanau().soap_envelope( + NFSE[self.autorizador]["CONSULTA_SERVICO"], payload + ) + return self._post_soap_raw(url, envelope_xml) + elif self.autorizador == "MARACANAU": + from pynfe.processamento.autorizador_nfse import SerializacaoGiss + + envelope_xml = SerializacaoGiss().soap_envelope( + NFSE[self.autorizador]["CONSULTA_SERVICO"], payload + ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": # comunica via wsdl @@ -874,6 +898,11 @@ def _get_url(self): ambiente = "HOMOLOGACAO" if self.autorizador in NFSE: self.url = NFSE[self.autorizador][ambiente] + if self.autorizador == "GISS": + municipio = obter_municipio_por_codigo( + self.municipio_codigo, uf=self.municipio_codigo[:2], normalizado=True + ) + self.url = self.url.replace("{municipio}", str(municipio.lower())) else: raise Exception("Autorizador nao encontrado!") return self.url @@ -901,6 +930,7 @@ def _post(self, url, xml, metodo): raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + def _post_soap_raw(self, url, soap_xml): certificado_a1 = CertificadoA1(self.certificado) key, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) @@ -909,7 +939,7 @@ def _post_soap_raw(self, url, soap_xml): data=soap_xml.encode("utf-8"), cert=(cert, key), verify=False, - headers={"Content-Type": "text/xml; charset=utf-8"} + headers={"Content-Type": "text/xml; charset=utf-8"}, ) def _post_https(self, url, metodo, xml): diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 0336633e..9fa84ca7 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -534,6 +534,11 @@ "HTTPS": "https://speedgov.com.br/wsmar/Nfes?wsdl", "HOMOLOGACAO": "", }, + "GISS": { + "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", + "HTTPS": "https://ws-{municipio}.giss.com.br/service-ws/nf/nfse-ws?wsdl", + "HOMOLOGACAO": "", + }, } # MDF-e From c0a50ebe19514f39c0707346a7abb81587d59992 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 20:49:03 -0300 Subject: [PATCH 158/175] =?UTF-8?q?altera=20autorizador=20na=20classe=20Co?= =?UTF-8?q?municacaoNfse=20de=20Maracana=C3=BA=20para=20GISS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index a6f143c3..ade2bd1d 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -826,7 +826,7 @@ def consultar_periodo(self, payload): NFSE[self.autorizador]["CONSULTA_SERVICO"], payload ) return self._post_soap_raw(url, envelope_xml) - elif self.autorizador == "MARACANAU": + elif self.autorizador == "GISS": from pynfe.processamento.autorizador_nfse import SerializacaoGiss envelope_xml = SerializacaoGiss().soap_envelope( From 86f78cad778bd32df0ed36674a81a7afbb3274f1 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 21:54:26 -0300 Subject: [PATCH 159/175] adiciona assinatura digital ao payload na classe ComunicacaoNfse para o autorizador GISS --- pynfe/processamento/comunicacao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ade2bd1d..864a4e8a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -828,9 +828,10 @@ def consultar_periodo(self, payload): return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "GISS": from pynfe.processamento.autorizador_nfse import SerializacaoGiss + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) envelope_xml = SerializacaoGiss().soap_envelope( - NFSE[self.autorizador]["CONSULTA_SERVICO"], payload + NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "OSASCO": From 799f1931881f54857e6fc0e97361085194669a68 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 22:03:22 -0300 Subject: [PATCH 160/175] =?UTF-8?q?altera=20nome=20do=20par=C3=A2metro=20d?= =?UTF-8?q?e=20'municipio=5Fcodigo'=20para=20'codigo=5Fmunicipio'=20na=20c?= =?UTF-8?q?lasse=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 864a4e8a..8d99a46a 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -756,11 +756,11 @@ def __init__( elif self.autorizador == "MARACANAU": pass elif self.autorizador == "GISS": - if not kwargs.get("municipio_codigo"): + if not kwargs.get("codigo_municipio"): raise Exception( "Para o autorizador GISS é necessário informar o código do município." ) - self.municipio_codigo = kwargs.get("municipio_codigo") + self.codigo_municipio = kwargs.get("codigo_municipio") else: raise Exception("Autorizador não encontrado!") @@ -901,7 +901,7 @@ def _get_url(self): self.url = NFSE[self.autorizador][ambiente] if self.autorizador == "GISS": municipio = obter_municipio_por_codigo( - self.municipio_codigo, uf=self.municipio_codigo[:2], normalizado=True + self.codigo_municipio, uf=self.codigo_municipio[:2], normalizado=True ) self.url = self.url.replace("{municipio}", str(municipio.lower())) else: From 464325f294106bbff10f236a902e6e98632241a2 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 22:05:03 -0300 Subject: [PATCH 161/175] =?UTF-8?q?altera=20m=C3=A9todo=20de=20assinatura?= =?UTF-8?q?=20na=20classe=20ComunicacaoNfse=20para=20retornar=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 8d99a46a..967e9064 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -828,7 +828,7 @@ def consultar_periodo(self, payload): return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "GISS": from pynfe.processamento.autorizador_nfse import SerializacaoGiss - xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload, retornar_string=True) envelope_xml = SerializacaoGiss().soap_envelope( NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado From 3fa99374466c013c63430a2ed96bc860831d3a50 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Sun, 4 Jan 2026 22:05:47 -0300 Subject: [PATCH 162/175] =?UTF-8?q?altera=20par=C3=A2metro=20'retornar=5Fs?= =?UTF-8?q?tring'=20para=20'retorna=5Fstring'=20no=20m=C3=A9todo=20de=20as?= =?UTF-8?q?sinatura=20da=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 967e9064..8af9b634 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -828,7 +828,7 @@ def consultar_periodo(self, payload): return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "GISS": from pynfe.processamento.autorizador_nfse import SerializacaoGiss - xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload, retornar_string=True) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload, retorna_string=True) envelope_xml = SerializacaoGiss().soap_envelope( NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado From d488cf47f36196b3ef601d02d06374fa3674a396 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 5 Jan 2026 10:44:20 -0300 Subject: [PATCH 163/175] =?UTF-8?q?altera=20classe=20SerializacaoMaracanau?= =?UTF-8?q?=20para=20SerializacaoSpeedgov=20e=20ajusta=20l=C3=B3gica=20de?= =?UTF-8?q?=20autoriza=C3=A7=C3=A3o=20na=20classe=20ComunicacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 4 +- pynfe/processamento/comunicacao.py | 23 +++++++--- pynfe/utils/webservices.py | 59 ++++++++++++++----------- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index e31faf67..b982d40f 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -113,9 +113,9 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): return etree.tostring(raiz, pretty_print=True).decode() -class SerializacaoMaracanau(InterfaceAutorizador): +class SerializacaoSpeedgov(InterfaceAutorizador): """ - Serialização ABRASF v1.00 – Maracanaú + Serialização ABRASF v1.00 – Speedgov """ def _cabecalho(self): diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 8af9b634..7090749b 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -753,8 +753,12 @@ def __init__( pass elif self.autorizador == "CAMPINAS": pass - elif self.autorizador == "MARACANAU": - pass + elif self.autorizador == "SPEEDGOV": + if not kwargs.get("codigo_municipio"): + raise Exception( + "Para o autorizador SPEEDGOV é necessário informar o código do município." + ) + self.codigo_municipio = kwargs.get("codigo_municipio") elif self.autorizador == "GISS": if not kwargs.get("codigo_municipio"): raise Exception( @@ -819,17 +823,19 @@ def consultar_periodo(self, payload): NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado ) return self._post_soap_raw(url, envelope_xml) - elif self.autorizador == "MARACANAU": - from pynfe.processamento.autorizador_nfse import SerializacaoMaracanau + elif self.autorizador == "SPEEDGOV": + from pynfe.processamento.autorizador_nfse import SerializacaoSpeedgov - envelope_xml = SerializacaoMaracanau().soap_envelope( + envelope_xml = SerializacaoSpeedgov().soap_envelope( NFSE[self.autorizador]["CONSULTA_SERVICO"], payload ) return self._post_soap_raw(url, envelope_xml) elif self.autorizador == "GISS": from pynfe.processamento.autorizador_nfse import SerializacaoGiss - xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar(payload, retorna_string=True) + xml_assinado = AssinaturaA1(self.certificado, self.certificado_senha).assinar( + payload, retorna_string=True + ) envelope_xml = SerializacaoGiss().soap_envelope( NFSE[self.autorizador]["CONSULTA_SERVICO"], xml_assinado ) @@ -904,6 +910,11 @@ def _get_url(self): self.codigo_municipio, uf=self.codigo_municipio[:2], normalizado=True ) self.url = self.url.replace("{municipio}", str(municipio.lower())) + if self.autorizador == "SPEEDGOV": + self.url = self.url.replace( + "{suffix_municipio}", + NFSE[self.autorizador]["SUFFIX_MUNICIPIO"][str(self.codigo_municipio)], + ) else: raise Exception("Autorizador nao encontrado!") return self.url diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 9fa84ca7..1b2f72e3 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -315,7 +315,7 @@ "HTTPS": "https://", "HOMOLOGACAO": "https://hom", }, - "MA": {"CADASTRO": ("https://sistemas.sefaz.ma.gov.br/wscadastro/CadConsultaCadastro2?wsdl")}, + "MA": {"CADASTRO": "https://sistemas.sefaz.ma.gov.br/wscadastro/CadConsultaCadastro2?wsdl"}, "PE": { "STATUS": "sefaz.pe.gov.br/nfe-service/services/NFeStatusServico4", "AUTORIZACAO": "sefaz.pe.gov.br/nfe-service/services/NFeAutorizacao4", @@ -328,14 +328,12 @@ "HOMOLOGACAO": "https://nfehomolog.", }, "BA": { - "STATUS": ("nfe.sefaz.ba.gov.br/webservices/NFeStatusServico4/NFeStatusServico4.asmx"), - "AUTORIZACAO": ("nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx"), - "RECIBO": ("nfe.sefaz.ba.gov.br/webservices/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx"), - "CHAVE": ( - "nfe.sefaz.ba.gov.br/webservices/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx" - ), - "INUTILIZACAO": ("nfe.sefaz.ba.gov.br/webservices/NFeInutilizacao4/NFeInutilizacao4.asmx"), - "EVENTOS": ("nfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx"), + "STATUS": "nfe.sefaz.ba.gov.br/webservices/NFeStatusServico4/NFeStatusServico4.asmx", + "AUTORIZACAO": "nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx", + "RECIBO": "nfe.sefaz.ba.gov.br/webservices/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx", + "CHAVE": "nfe.sefaz.ba.gov.br/webservices/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx", + "INUTILIZACAO": "nfe.sefaz.ba.gov.br/webservices/NFeInutilizacao4/NFeInutilizacao4.asmx", + "EVENTOS": "nfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx", "CADASTRO": ( "nfe.sefaz.ba.gov.br/webservices/CadConsultaCadastro4/CadConsultaCadastro4.asmx" ), @@ -365,10 +363,10 @@ "HOMOLOGACAO": "https://homologacao.", }, "PR": { - "STATUS": ("nfe.sefa.pr.gov.br/nfe/NFeStatusServico4"), # CONSULTA STATUS DO SERVICO + "STATUS": "nfe.sefa.pr.gov.br/nfe/NFeStatusServico4", # CONSULTA STATUS DO SERVICO "AUTORIZACAO": "nfe.sefa.pr.gov.br/nfe/NFeAutorizacao4", # AUTORIZACAO "RECIBO": "nfe.sefa.pr.gov.br/nfe/NFeRetAutorizacao4", # CONSULTA RECIBO - "CHAVE": ("nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4"), # CONSULTA CHAVE DE ACESSO + "CHAVE": "nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4", # CONSULTA CHAVE DE ACESSO "INUTILIZACAO": "nfe.sefa.pr.gov.br/nfe/NFeInutilizacao4", # INUTILIZAÇAO "EVENTOS": "nfe.sefa.pr.gov.br/nfe/NFeRecepcaoEvento4", # REGISTRO DE EVENTOS "CADASTRO": "nfe.sefa.pr.gov.br/nfe/CadConsultaCadastro4", # CONSULTA CADASTRO @@ -425,12 +423,12 @@ "HOMOLOGACAO": "https://homolog.", }, "SVAN": { - "STATUS": ("sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx"), - "AUTORIZACAO": ("sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx"), - "RECIBO": ("sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx"), + "STATUS": "sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx", + "AUTORIZACAO": "sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx", + "RECIBO": "sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx", "CHAVE": "sefazvirtual.fazenda.gov.br/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx", - "INUTILIZACAO": ("sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx"), - "EVENTOS": ("sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx"), + "INUTILIZACAO": "sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx", + "EVENTOS": "sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx", "DOWNLOAD": "sefazvirtual.fazenda.gov.br/NfeDownloadNF/NfeDownloadNF.asmx", "HTTPS": "https://www.", "HOMOLOGACAO": "https://hom.", @@ -478,14 +476,14 @@ "CANCELAR_NFSE": "CancelamentoNFe", # New URL supports both v1 and v2 (Reforma Tributária 2026) # Old URL (v1 only): https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl - "HTTPS": "https://nfews.prefeitura.sp.gov.br/lotenfe.asmx?WSDL" + "HTTPS": "https://nfews.prefeitura.sp.gov.br/lotenfe.asmx?WSDL", }, "BARUERI": { "ENVIAR_RPS": "EnviarRPS", "CONSULTA_RPS": "ConsultaNFe", "CANCELAR_NFSE": "CancelamentoNFe", "HTTPS": "https://www.barueri.sp.gov.br/nfeservice/wsrps.asmx?WSDL", - "HOMOLOGACAO": "https://testeeiss.barueri.sp.gov.br/nfeservice/wsrps.asmx?WSDL" + "HOMOLOGACAO": "https://testeeiss.barueri.sp.gov.br/nfeservice/wsrps.asmx?WSDL", }, "BETHA": { "AUTORIZACAO": "GerarNfse", @@ -496,7 +494,7 @@ "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", "SUBSTITUIR": "SubstituirNfse", "HTTPS": "http://e-gov.betha.com.br/e-nota-contribuinte-ws/nfseWS?wsdl", - "HOMOLOGACAO": ("http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl"), + "HOMOLOGACAO": "http://e-gov.betha.com.br/e-nota-contribuinte-test-ws/nfseWS?wsdl", }, # "GINFES": { @@ -515,9 +513,8 @@ "CONSULTA": "Consultar", "CONSULTA_COMPLETA": "ConsultarNotaCompleta", "HTTPS": "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl", - "HOMOLOGACAO": ( - "https://nfe.osasco.sp.gov.br/EISSNFEWebServices/NotaFiscalEletronica.svc?wsdl" - ), + "HOMOLOGACAO": "", + "DOWNLOAD": "https://nfe.osasco.sp.gov.br/EissnfeWebApp/Sistema/Prestador/VisualizarNFENew.aspx?Id={identificador}", }, "CAMPINAS": { "AUTORIZACAO": "GerarNfse", @@ -527,17 +524,29 @@ "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", "CONSULTA_SERVICO_TOMADO": "ConsultarNfseServicoTomado", "HTTPS": "https://novanfse.campinas.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", - "HOMOLOGACAO": "https://homol-rps.ima.sp.gov.br/notafiscal-abrasfv203-ws/NotaFiscalSoap?wsdl", + "HOMOLOGACAO": "", + "DOWNLOAD": ( + "https://novanfse.campinas.sp.gov.br/notafiscal-ws/servico/notafiscal/autenticacao/cpfCnpj/{cpf_cnpj}/inscricaoMunicipal/{im}/numeroNota/{numero_nfse}/codigoVerificacao/{codigo_verificacao}" + ), }, - "MARACANAU": { + "SPEEDGOV": { "CONSULTA_SERVICO": "ConsultarNfse", - "HTTPS": "https://speedgov.com.br/wsmar/Nfes?wsdl", + "HTTPS": "https://speedgov.com.br/ws{suffix_municipio}/Nfes?wsdl", "HOMOLOGACAO": "", + "DOWNLOAD": ( + "https://speedgov.com.br/sat{suffix_municipio}/servlet//com.satweb.aratb177e?1,1,1,1,{im},{numero_nfse},{codigo_verificacao}" + ), + "SUFFIX_MUNICIPIO": { + "2307650":"mar" + }, }, "GISS": { "CONSULTA_SERVICO": "ConsultarNfseServicoPrestado", "HTTPS": "https://ws-{municipio}.giss.com.br/service-ws/nf/nfse-ws?wsdl", "HOMOLOGACAO": "", + "DOWNLOAD": ( + "https://gissv2-{codigo_ibge}.eiconbrasil.com.br/service-relatorio/api/relatorio/nota-autenticacao/{tipo_doc}/{codigo_ibge}/{identificador}/codigo-verificacao/{codigo_verificacao}" + ), }, } From 893fe3bd0f9785e097e3354f48a4180fd820b0ef Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 5 Jan 2026 12:08:58 -0300 Subject: [PATCH 164/175] adiciona suporte para novos autorizadores GISS e Speedgov na classe SerializacaoNfse --- pynfe/processamento/serializacao.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 760186a4..46a5056a 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2158,6 +2158,14 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): from pynfe.processamento.autorizador_nfse import SerializacaoOsasco return SerializacaoOsasco(self.chave_autenticacao).consultar(data_inicial=data_inicio, data_final=data_fim) + elif self.autorizador.lower() == "giss": + from pynfe.processamento.autorizador_nfse import SerializacaoGiss + + return SerializacaoGiss(self.chave_autenticacao).consultar_periodo(emitente, data_inicio, data_fim, pagina=1) + elif self.autorizador.lower() == "speedgov": + from pynfe.processamento.autorizador_nfse import SerializacaoSpeedgov + + return SerializacaoSpeedgov(self.chave_autenticacao).consultar_periodo(emitente, data_inicio, data_fim) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") From fdaeafacd864872fa0e385c1e4d124f4ec8312a0 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 5 Jan 2026 12:59:34 -0300 Subject: [PATCH 165/175] =?UTF-8?q?corrige=20inst=C3=A2ncia=20de=20Seriali?= =?UTF-8?q?zacaoGiss=20e=20SerializacaoSpeedgov=20na=20classe=20Serializac?= =?UTF-8?q?aoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/serializacao.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 46a5056a..ec42071f 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2161,11 +2161,11 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): elif self.autorizador.lower() == "giss": from pynfe.processamento.autorizador_nfse import SerializacaoGiss - return SerializacaoGiss(self.chave_autenticacao).consultar_periodo(emitente, data_inicio, data_fim, pagina=1) + return SerializacaoGiss().consultar_periodo(emitente, data_inicio, data_fim, pagina=1) elif self.autorizador.lower() == "speedgov": from pynfe.processamento.autorizador_nfse import SerializacaoSpeedgov - return SerializacaoSpeedgov(self.chave_autenticacao).consultar_periodo(emitente, data_inicio, data_fim) + return SerializacaoSpeedgov().consultar_periodo(emitente, data_inicio, data_fim) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") From d110d60c6ba3ae6f2c58f9c920057da6dc04b002 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 5 Jan 2026 16:09:38 -0300 Subject: [PATCH 166/175] =?UTF-8?q?corrige=20convers=C3=A3o=20de=20c=C3=B3?= =?UTF-8?q?digo=20municipal=20para=20string=20na=20classe=20ComunicacaoNfs?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 7090749b..b5111744 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -907,7 +907,7 @@ def _get_url(self): self.url = NFSE[self.autorizador][ambiente] if self.autorizador == "GISS": municipio = obter_municipio_por_codigo( - self.codigo_municipio, uf=self.codigo_municipio[:2], normalizado=True + str(self.codigo_municipio), uf=str(self.codigo_municipio)[:2], normalizado=True ) self.url = self.url.replace("{municipio}", str(municipio.lower())) if self.autorizador == "SPEEDGOV": From e778aa376f20a18d549107a61a95488136bea8fb Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Tue, 6 Jan 2026 18:15:07 -0300 Subject: [PATCH 167/175] =?UTF-8?q?ajusta=20passagem=20do=20par=C3=A2metro?= =?UTF-8?q?=20'pagina'=20nos=20m=C3=A9todos=20de=20consulta=20da=20classe?= =?UTF-8?q?=20SerializacaoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/serializacao.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index ec42071f..08c456b6 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2142,7 +2142,7 @@ def consultar_faixa(self, emitente, numero_inicial, numero_final, pagina=1): if self.autorizador.lower() == "campinas": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - return SerializacaoCampinas().consultar_faixa(emitente, numero_inicial, numero_final, pagina=1) + return SerializacaoCampinas().consultar_faixa(emitente, numero_inicial, numero_final, pagina=pagina) elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco return SerializacaoOsasco(self.chave_autenticacao).consultar(numero_nota_inicial=numero_inicial, numero_nota_final=numero_final) @@ -2153,7 +2153,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): if self.autorizador.lower() == "campinas": from pynfe.processamento.autorizador_nfse import SerializacaoCampinas - return SerializacaoCampinas().consultar_periodo(emitente, data_inicio, data_fim, pagina=1) + return SerializacaoCampinas().consultar_periodo(emitente, data_inicio, data_fim, pagina=pagina) elif self.autorizador.lower() == "osasco": from pynfe.processamento.autorizador_nfse import SerializacaoOsasco @@ -2161,7 +2161,7 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): elif self.autorizador.lower() == "giss": from pynfe.processamento.autorizador_nfse import SerializacaoGiss - return SerializacaoGiss().consultar_periodo(emitente, data_inicio, data_fim, pagina=1) + return SerializacaoGiss().consultar_periodo(emitente, data_inicio, data_fim, pagina=pagina) elif self.autorizador.lower() == "speedgov": from pynfe.processamento.autorizador_nfse import SerializacaoSpeedgov From 0feaf9a7e3710e8534b4b3ea117b4b3d69b360f8 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:19:04 -0300 Subject: [PATCH 168/175] =?UTF-8?q?adiciona=20suporte=20para=20o=20autoriz?= =?UTF-8?q?ador=20GINFES=20na=20classe=20ComunicacaoNfse=20e=20renomeia=20?= =?UTF-8?q?m=C3=A9todo=20de=20consulta=20na=20classe=20SerializacaoGinfes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/autorizador_nfse.py | 3 ++- pynfe/processamento/comunicacao.py | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pynfe/processamento/autorizador_nfse.py b/pynfe/processamento/autorizador_nfse.py index b982d40f..7a8a5747 100644 --- a/pynfe/processamento/autorizador_nfse.py +++ b/pynfe/processamento/autorizador_nfse.py @@ -492,8 +492,9 @@ def serializar_lote_sincrono(self, nfse): class SerializacaoGinfes(InterfaceAutorizador): def __init__(self): pass + - def consultar_servico_prestado(self, emitente, data_inicio, data_fim, pagina=1): + def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): NS = "http://www.ginfes.com.br/servico_consultar_nfse_servico_prestado_envio_v03.xsd" DS = "http://www.w3.org/2000/09/xmldsig#" diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index b5111744..d2ddc279 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -744,7 +744,10 @@ def __init__( self.certificado_senha = certificado_senha self._ambiente = 2 if homologacao else 1 self.autorizador = autorizador.upper() - if self.autorizador == "SAO_PAULO": + if self.autorizador == "GINFES": + self._namespace = "http://www.ginfes.com.br/cabecalho_v03.xsd" + self._versao = "3" + elif self.autorizador == "SAO_PAULO": self._namespace = "http://www.prefeitura.sp.gov.br/nfe" self._versao = "2" elif self.autorizador == "BARUERI": @@ -843,6 +846,11 @@ def consultar_periodo(self, payload): elif self.autorizador == "OSASCO": # comunica via wsdl return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) + elif self.autorizador == "GINFES": + # xml + xml = '' + xml + # comunica via wsdl + return self._post_https(url, xml, "ConsultarNfseV3") else: raise Exception("Este método não esta implementado para o autorizador.") From 901d91ded2a838a067351a0d9831ce887bfb98cc Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:21:49 -0300 Subject: [PATCH 169/175] adiciona suporte para o autorizador GINFES na classe SerializacaoNfse --- pynfe/processamento/serializacao.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 08c456b6..fe3033ba 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -2166,6 +2166,10 @@ def consultar_periodo(self, emitente, data_inicio, data_fim, pagina=1): from pynfe.processamento.autorizador_nfse import SerializacaoSpeedgov return SerializacaoSpeedgov().consultar_periodo(emitente, data_inicio, data_fim) + elif self.autorizador.lower() == "ginfes": + from pynfe.processamento.autorizador_nfse import SerializacaoGinfes + + return SerializacaoGinfes().consultar_periodo(emitente, data_inicio, data_fim, pagina=pagina) else: raise Exception(f"Este método não esta implementado para o autorizador {self.autorizador.upper()}") From 2cbc954707acacc1034cd26f60f6eb5a885fed52 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:28:19 -0300 Subject: [PATCH 170/175] =?UTF-8?q?corrige=20constru=C3=A7=C3=A3o=20do=20X?= =?UTF-8?q?ML=20na=20classe=20ComunicacaoNfse=20para=20usar=20o=20payload?= =?UTF-8?q?=20correto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index d2ddc279..2ce99eff 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -848,7 +848,7 @@ def consultar_periodo(self, payload): return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) elif self.autorizador == "GINFES": # xml - xml = '' + xml + xml = '' + payload # comunica via wsdl return self._post_https(url, xml, "ConsultarNfseV3") else: From a11b78bd17c76b413fd9ce1fd28a60211b7725e3 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:30:42 -0300 Subject: [PATCH 171/175] =?UTF-8?q?corrige=20codifica=C3=A7=C3=A3o=20do=20?= =?UTF-8?q?payload=20na=20classe=20ComunicacaoNfse=20para=20UTF-8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 2ce99eff..c5289d71 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -848,7 +848,7 @@ def consultar_periodo(self, payload): return self._post_zeep(url, NFSE[self.autorizador]["CONSULTA"], payload) elif self.autorizador == "GINFES": # xml - xml = '' + payload + xml = '' + payload.decode("utf-8") # comunica via wsdl return self._post_https(url, xml, "ConsultarNfseV3") else: From 0b083993e50d2f7c4dda2cdd5379c067307dbfc2 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:32:47 -0300 Subject: [PATCH 172/175] =?UTF-8?q?ajusta=20ordem=20dos=20par=C3=A2metros?= =?UTF-8?q?=20no=20m=C3=A9todo=20=5Fpost=5Fhttps=20da=20classe=20Comunicac?= =?UTF-8?q?aoNfse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index c5289d71..2723b686 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -850,7 +850,7 @@ def consultar_periodo(self, payload): # xml xml = '' + payload.decode("utf-8") # comunica via wsdl - return self._post_https(url, xml, "ConsultarNfseV3") + return self._post_https(url, "ConsultarNfseV3", xml) else: raise Exception("Este método não esta implementado para o autorizador.") From 033b28696a20085cb39e596b20127dad668be67c Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Mon, 12 Jan 2026 08:54:24 -0300 Subject: [PATCH 173/175] =?UTF-8?q?ajusta=20m=C3=A9todo=20=5Fpost=5Fhttps?= =?UTF-8?q?=20na=20classe=20ComunicacaoNfse=20para=20aceitar=20par=C3=A2me?= =?UTF-8?q?tros=20na=20ordem=20correta=20e=20implementa=20l=C3=B3gica=20pa?= =?UTF-8?q?ra=20diferentes=20tipos=20de=20chamadas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 37 ++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 2723b686..c2befd66 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -850,7 +850,7 @@ def consultar_periodo(self, payload): # xml xml = '' + payload.decode("utf-8") # comunica via wsdl - return self._post_https(url, "ConsultarNfseV3", xml) + return self._post_https(url, xml, "consulta") else: raise Exception("Este método não esta implementado para o autorizador.") @@ -962,7 +962,7 @@ def _post_soap_raw(self, url, soap_xml): headers={"Content-Type": "text/xml; charset=utf-8"}, ) - def _post_https(self, url, metodo, xml): + def _post_https(self, url, xml, metodo): """Comunicação wsdl (https) utilizando certificado do usuário""" # cabecalho cabecalho = self._cabecalho() @@ -971,21 +971,38 @@ def _post_https(self, url, metodo, xml): from pynfe.utils.https_nfse import HttpAuthenticated from suds.client import Client - certificado_a1 = CertificadoA1(self.certificado) - - chave, cert = certificado_a1.separar_arquivo(self.certificado_senha, caminho=True) + certificadoA1 = CertificadoA1(self.certificado) + chave, cert = certificadoA1.separar_arquivo(self.certificado_senha, caminho=True) cliente = Client(url, transport=HttpAuthenticated(key=chave, cert=cert, endereco=url)) # gerar nfse - try: - service = getattr(cliente.service, metodo) - except AttributeError: - raise ValueError(f"Método '{metodo}' não disponível para {self.autorizador}.") - return service(xml) + if metodo == "gerar": + return cliente.service.GerarNfse(cabecalho, xml) + elif metodo == "enviar_lote": + return cliente.service.RecepcionarLoteRpsV3(cabecalho, xml) + elif metodo == "consulta": + return cliente.service.ConsultarNfseV3(cabecalho, xml) + elif metodo == "consulta_lote": + return cliente.service.ConsultarLoteRpsV3(cabecalho, xml) + elif metodo == "consulta_situacao_lote": + return cliente.service.ConsultarSituacaoLoteRpsV3(cabecalho, xml) + elif metodo == "consultaRps": + return cliente.service.ConsultarNfsePorRpsV3(cabecalho, xml) + elif metodo == "consultaFaixa": + return cliente.service.ConsultarNfseFaixa(cabecalho, xml) + elif metodo == "cancelar": + # versão 2 + return cliente.service.CancelarNfse(xml) + # versão 3 + # return cliente.service.CancelarNfseV3(cabecalho, xml) + # TODO outros metodos + else: + raise Exception("Método não implementado no autorizador.") except Exception as e: raise e + def enviar_barueri(self, xml, operation): url = self._get_url() From 059d069181c03d032700af0db456a5d04a53b240 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 30 Jan 2026 11:43:50 -0300 Subject: [PATCH 174/175] =?UTF-8?q?atualiza=20lista=20de=20estados=20para?= =?UTF-8?q?=20conting=C3=AAncia=20no=20m=C3=A9todo=20=5Fget=5Furl=20da=20c?= =?UTF-8?q?lasse=20ComunicacaoSefaz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index c2befd66..e43f4efc 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -508,7 +508,7 @@ def _get_url_an(self, consulta): def _get_url(self, modelo, consulta, contingencia=False): """Retorna a url para comunicação com o webservice""" if contingencia: - contingencia_svrs = ["AM", "BA", "CE", "GO", "MA", "MS", "MT", "PE", "PR"] + contingencia_svrs = ["SP", "AM", "BA", "CE", "GO", "MA", "MS", "MT", "PE", "PR"] contingencia_svan = [ "AC", "AL", @@ -526,7 +526,6 @@ def _get_url(self, modelo, consulta, contingencia=False): "RS", "SC", "SE", - "SP", "TO", ] From bba39386a8574839da4554890dc537540bde71c9 Mon Sep 17 00:00:00 2001 From: Lucas Svizzero Ribeiro Date: Fri, 30 Jan 2026 11:45:59 -0300 Subject: [PATCH 175/175] =?UTF-8?q?adiciona=20estado=20SP=20=C3=A0=20lista?= =?UTF-8?q?=20de=20conting=C3=AAncia=20no=20m=C3=A9todo=20=5Fget=5Furl=20d?= =?UTF-8?q?a=20classe=20ComunicacaoSefaz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pynfe/processamento/comunicacao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index e43f4efc..c2befd66 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -508,7 +508,7 @@ def _get_url_an(self, consulta): def _get_url(self, modelo, consulta, contingencia=False): """Retorna a url para comunicação com o webservice""" if contingencia: - contingencia_svrs = ["SP", "AM", "BA", "CE", "GO", "MA", "MS", "MT", "PE", "PR"] + contingencia_svrs = ["AM", "BA", "CE", "GO", "MA", "MS", "MT", "PE", "PR"] contingencia_svan = [ "AC", "AL", @@ -526,6 +526,7 @@ def _get_url(self, modelo, consulta, contingencia=False): "RS", "SC", "SE", + "SP", "TO", ]