Skip to content
115 changes: 115 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# AGENTS.md - PyNFe

Brazilian electronic fiscal document library (NF-e, NFC-e, NFS-e, MDF-e, CT-e) for SEFAZ webservice communication.

## Source Map Navigation (MANDATORY)

**Before reading any large file (>200 lines), you MUST first read its `{filename}_map.md` file** in the `docs/` directory. The source map contains:
- Section-by-section breakdown with exact line ranges
- Class/method index with purpose descriptions
- Field group documentation

This allows you to navigate directly to the specific line-window you need instead of reading the entire file.

### Available Source Maps

| Source Map | File | Lines | Description |
|------------|------|-------|-------------|
| `docs/serializacao_map.md` | `pynfe/processamento/serializacao.py` | 2630 | XML serialization (NF-e, MDF-e, QR codes) |
| `docs/comunicacao_map.md` | `pynfe/processamento/comunicacao.py` | 1348 | SEFAZ webservice communication |
| `docs/autorizador_nfse_map.md` | `pynfe/processamento/autorizador_nfse.py` | 538 | NFS-e authorization (Betha/Ginfes) |
| `docs/notafiscal_map.md` | `pynfe/entidades/notafiscal.py` | 1253 | Invoice entities and tax fields |
| `docs/manifesto_map.md` | `pynfe/entidades/manifesto.py` | 447 | MDF-e manifest entities |
| `docs/evento_map.md` | `pynfe/entidades/evento.py` | 237 | Event entities (cancel, correction, etc.) |
| `docs/flags_map.md` | `pynfe/utils/flags.py` | 645 | Constants, namespaces, tax codes |
| `docs/webservices_map.md` | `pynfe/utils/webservices.py` | 572 | SEFAZ endpoint URLs by state |
| `docs/utils_map.md` | `pynfe/utils/__init__.py` | 253 | Utility functions (municipality lookup, signing) |

### How to Use Source Maps

1. **Read the `_map.md` file first** to understand the file structure
2. **Identify the line range** you need from the map tables
3. **Read only that section** using `offset` and `limit` parameters
4. Example: To understand ICMS CST 60 serialization, read `docs/serializacao_map.md`, find it's at lines 747-770, then read `pynfe/processamento/serializacao.py` with `offset=747, limit=25`

## Project Structure

```
docs/ # Documentation and source maps (*_map.md, reforma_tributaria.md)
pynfe/
├── entidades/ # Domain entities (data models)
│ ├── base.py # Base entity class with kwargs init
│ ├── certificado.py # A1 certificate handling
│ ├── cliente.py # Customer entity
│ ├── emitente.py # Issuer entity
│ ├── evento.py # Event entities (cancel, correction, MDF-e events)
│ ├── manifesto.py # MDF-e manifest entity
│ ├── notafiscal.py # NF-e/NFC-e invoice entity + products + taxes
│ ├── produto.py # Product entity (standalone)
│ ├── servico.py # Service entity (NFS-e)
│ └── transportadora.py # Carrier entity
├── processamento/ # Core processing logic
│ ├── assinatura.py # XML digital signing with A1 certificates
│ ├── autorizador_nfse.py # NFS-e serialization (Betha/Ginfes PyXB bindings)
│ ├── comunicacao.py # SEFAZ SOAP webservice communication
│ ├── serializacao.py # XML serialization (entities → SEFAZ XML)
│ └── validacao.py # XML schema validation
├── utils/ # Utilities
│ ├── __init__.py # Municipality lookup, XML signing helpers
│ ├── flags.py # Constants, namespaces, tax code enumerations
│ ├── webservices.py # SEFAZ endpoint URLs by state/environment
│ ├── bar_code_128.py # Code 128 barcode generation for DANFE
│ ├── xml_writer.py # XML element writing helpers
│ └── nfse/ # NFS-e provider-specific PyXB bindings (GENERATED - do not edit)
│ ├── betha/ # Betha provider bindings (13,941+ lines)
│ └── ginfes/ # Ginfes provider bindings (8,028+ lines)
├── data/ # Reference data files
│ ├── IBPT/ # Tax tables by state (CSV)
│ ├── ISSQN/ # Service tax classification
│ ├── MunIBGE/ # Municipality IBGE codes by UF
│ └── XSDs/ # XML Schema definitions (NF-e, NFC-e, NFS-e, MDF-e, CT-e)
tests/ # Test suite (37 test files)
```

## Key Concepts

- **NF-e** (modelo 55): Standard electronic invoice
- **NFC-e** (modelo 65): Consumer electronic invoice (retail)
- **NFS-e**: Municipal service invoice (Betha/Ginfes providers)
- **MDF-e** (modelo 58): Transport manifest
- **CT-e**: Transport knowledge document (partial support)
- **SEFAZ**: State tax authority webservices
- **Homologacao**: Test environment (`_ambiente=2`)
- **SVRS/SVAN**: Virtual SEFAZ environments for states without own webservices

## Commands

```bash
# Run tests
pytest tests/

# Run specific test
pytest tests/test_nfe_serializacao_geral.py

# Lint
ruff check pynfe/

# Format
ruff format pynfe/
```

## Dependencies

- `lxml` — XML processing
- `signxml` — XML digital signatures
- `cryptography` / `pyopenssl` — Certificate handling
- `requests` — HTTP communication with SEFAZ
- `suds-community` — SOAP client (NFS-e only)
- `PyXB-X` — XML Schema bindings (NFS-e only)

## Important Notes

- Files under `pynfe/utils/nfse/` are **auto-generated** PyXB bindings — do not edit manually
- The `pynfe/data/` directory contains reference data files that should not be modified casually
- Tax code serialization follows strict SEFAZ XML schema ordering — field order matters
- Each Brazilian state has its own SEFAZ endpoint configuration in `webservices.py`
36 changes: 36 additions & 0 deletions docs/autorizador_nfse_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Source Map: `autorizador_nfse.py` (538 lines)

NFS-e authorization and serialization for Betha and Ginfes providers.

## Classes Overview

| Class | Lines | Purpose |
|-------|-------|---------|
| `InterfaceAutorizador` | 5-11 | Abstract interface (consultar_rps, cancelar) |
| `SerializacaoBetha` | 14-200 | Betha NFS-e XML generation using PyXB bindings |
| `SerializacaoGinfes` | ~202-538 | Ginfes NFS-e XML generation using PyXB bindings |

## `SerializacaoBetha` — Lines 14-200

### Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `__init__()` | 15-18 | Import Betha NFS-e v2.02 schema |
| `gerar()` | 20-94 | Generate NFS-e XML (service, provider, customer, RPS) |
| `consultar_rps()` | 96-130 | Query NFS-e by RPS |
| `consultar_faixa()` | ~132-160 | Query NFS-e by range |
| `cancelar()` | ~162-200 | Cancel NFS-e |

## `SerializacaoGinfes` — Lines ~202-538

### Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `__init__()` | ~202-210 | Import Ginfes schema modules |
| `cabecalho()` | ~212-220 | Generate XML header |
| `serializar_lote_assincrono()` | ~222-310 | Serialize async batch (RPS list) |
| `consultar_nfse()` | ~312-360 | Query NFS-e by provider/period |
| `consultar_lote()` | ~362-390 | Query batch by number |
| `consultar_rps()` | ~392-420 | Query NFS-e by RPS |
| `consultar_situacao_lote()` | ~422-450 | Query batch status |
| `cancelar()` / `cancelar_v2()` | ~452-538 | Cancel NFS-e (v2 and v3) |
120 changes: 120 additions & 0 deletions docs/comunicacao_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Source Map: `comunicacao.py` (1348 lines)

SEFAZ webservice communication for NF-e, NFC-e, NFS-e, MDF-e and CT-e.

## Classes Overview

| Class | Lines | Purpose |
|-------|-------|---------|
| `Comunicacao` | 31-47 | Abstract base class |
| `ComunicacaoSefaz` | 50-612 | NF-e/NFC-e SEFAZ communication |
| `ComunicacaoNfse` | 615-845 | NFS-e municipal communication (Betha/Ginfes) |
| `ComunicacaoMDFe` | 848-1119 | MDF-e SEFAZ communication |
| `ComunicacaoCTe` | 1121-1348 | CT-e SEFAZ communication |

---

## `Comunicacao` (base) — Lines 31-47

Stores `uf`, `certificado`, `certificado_senha`, `_ambiente` (1=prod, 2=homolog).

## `ComunicacaoSefaz` — Lines 50-612

### Public Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `autorizacao()` | 56-130 | Send NF-e for authorization (sync/async) |
| `consulta_recibo()` | 132-155 | Query batch processing result |
| `consulta_nota()` | 157-175 | Query NF-e status by access key |
| `consulta_distribuicao()` | 177-231 | DF-e distribution query (distNSU, consNSU, consChNFe) |
| `consulta_cadastro()` | 233-292 | Taxpayer registration query |
| `evento()` | 294-320 | Send NF-e events (cancel, correction letter) |
| `status_servico()` | 322-335 | Check SEFAZ server status |
| `inutilizacao()` | 337-410 | Number range invalidation |

### Internal Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `_get_url_an()` | 412-424 | Get national environment URL (AN) |
| `_get_url()` | 426-551 | **URL resolver** — routes to correct SEFAZ by UF, model, environment, contingency |
| `_construir_xml_soap()` | 553-570 | Build SOAP XML envelope |
| `_post_header()` | 572-581 | HTTP headers (PE requires SOAPAction) |
| `_post()` | 583-612 | Execute HTTPS POST with A1 certificate |

### URL Routing Detail (`_get_url`)
| Section | Lines | States |
|---------|-------|--------|
| Contingency SVRS | 429-462 | AM, BA, CE, GO, MA, MS, MT, PE, PR |
| Contingency SVAN | 463-476 | AC, AL, AP, DF, ES, MG, PA, PB, PI, RJ, RN, RO, RR, RS, SC, SE, SP, TO |
| Own webservices | 480-501 | PR, MS, SP, AM, CE, BA, GO, MG, MT, PE, RS |
| SVRS states | 504-533 | AC, AL, AP, DF, ES, PB, PI, RJ, RN, RO, RR, SC, SE, TO, PA |
| SVAN (MA only) | 536-548 | MA |

---

## `ComunicacaoNfse` — Lines 615-845

### Public Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `autorizacao()` | 635-644 | Generate NFS-e (Betha only) |
| `enviar_lote()` | 646-655 | Send batch (Ginfes only) |
| `consultar()` | 657-666 | Query NFS-e (Ginfes only) |
| `consultar_rps()` | 668-678 | Query by RPS (Betha + Ginfes) |
| `consultar_faixa()` | 680-687 | Query range (Betha only) |
| `consultar_lote()` | 689-698 | Query batch (Ginfes only) |
| `consultar_situacao_lote()` | 700-707 | Query batch status (Ginfes only) |
| `cancelar()` | 709-722 | Cancel NFS-e (Betha + Ginfes) |

### Internal Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `_cabecalho()` / `_cabecalho2()` | 724-762 | WSDL request header XML |
| `_cabecalho_ginfes()` | 764-768 | Ginfes-specific header via XSD |
| `_get_url()` | 770-780 | URL resolver for NFS-e |
| `_post()` | 782-804 | HTTP (no cert) WSDL communication |
| `_post_https()` | 806-845 | HTTPS (with cert) WSDL communication |

---

## `ComunicacaoMDFe` — Lines 848-1119

### Public Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `autorizacao()` | 874-956 | Send MDF-e for authorization (sync/async) |
| `status_servico()` | 958-965 | Check MDF-e server status |
| `consulta()` | 967-976 | Query MDF-e by access key |
| `consulta_nao_encerrados()` | 978-990 | Query non-closed MDF-e |
| `consulta_recibo()` | 992-1000 | Query batch processing result |
| `evento()` | 1002-1017 | Send MDF-e events (cancel, close, add driver, add DF-e, payment) |

### Internal Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `_construir_xml_soap()` | 1019-1044 | Build SOAP XML with header |
| `_post_header()` | 1046-1061 | HTTP headers |
| `_post()` | 1063-1099 | Execute HTTPS POST with cert |
| `_cabecalho_soap()` | 1101-1107 | SOAP header with cUF and versaoDados |
| `_get_url()` | 1109-1118 | URL resolver (SVRS only) |

---

## `ComunicacaoCTe` — Lines 1121-1348

### Public Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `status_servico()` | 1136-1147 | Check CT-e server status |
| `consulta_distribuicao()` | 1149-1200 | CT-e distribution query |
| `consulta()` | 1202-1211 | Query CT-e by access key |

### Internal Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `_get_url_an()` | 1213-1219 | National environment URL |
| `_cabecalho_soap()` | 1221-1227 | SOAP header |
| `_get_url()` | 1229-1281 | **URL resolver** — own (MT, MS, MG, PR, RS, SP), SVRS, SVSP |
| `_construir_xml_soap()` | 1283-1304 | Build SOAP XML envelope |
| `_post_header()` | 1306-1313 | HTTP headers |
| `_post()` | 1315-1348 | Execute HTTPS POST with cert |
38 changes: 38 additions & 0 deletions docs/evento_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Source Map: `evento.py` (237 lines)

Event entity models for NF-e and MDF-e lifecycle operations.

## Classes Overview

| Class | Lines | Purpose |
|-------|-------|---------|
| `Evento` | 11-46 | Base event class |
| `EventoCancelarNota` | 49-60 | NF-e cancellation (tp_evento=110111) |
| `EventoCartaCorrecao` | 63-90 | NF-e correction letter (tp_evento=110110) |
| `EventoManifestacaoDest` | 93-130 | Recipient manifestation (confirm/unknown/not performed/unaware) |
| `EventoOperacaoNaoRealizada` | ~132-145 | Operation not performed |
| `EventoCancelarMDFe` | ~147-160 | MDF-e cancellation |
| `EventoEncerrarMDFe` | ~162-180 | MDF-e closure |
| `EventoIncluirCondutorMDFe` | ~182-195 | Add driver to MDF-e |
| `EventoIncluirDFeMDFe` | ~197-215 | Add DF-e to MDF-e |
| `EventoPagamentoMDFe` | ~217-237 | MDF-e operation payment |

## `Evento` (base) — Lines 11-46

Fields: `id`, `orgao`, `cnpj`, `chave`, `data_emissao`, `uf`, `tp_evento`, `n_seq_evento`, `descricao`.

Property `identificador` builds: `"ID" + tp_evento + chave + n_seq_evento(2 digits)`.

## Key Event Types

| Class | tp_evento | descricao |
|-------|-----------|-----------|
| `EventoCancelarNota` | 110111 | "Cancelamento" |
| `EventoCartaCorrecao` | 110110 | "Carta de Correcao" |
| `EventoManifestacaoDest` | 210200-210240 | Various manifestation types |
| `EventoOperacaoNaoRealizada` | 110112 | "Operacao nao Realizada" |
| `EventoCancelarMDFe` | 110111 | "Cancelamento" (MDF-e) |
| `EventoEncerrarMDFe` | 110112 | "Encerramento" |
| `EventoIncluirCondutorMDFe` | 110114 | "Inclusão Condutor" |
| `EventoIncluirDFeMDFe` | 110115 | "Inclusao DF-e" |
| `EventoPagamentoMDFe` | 110116 | "Pagamento Operacao MDF-e" |
38 changes: 38 additions & 0 deletions docs/flags_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Source Map: `flags.py` (681 lines)

Constants, namespaces, tax code enumerations, and state codes used throughout PyNFe.

## Sections

| Section | Lines | Purpose |
|---------|-------|---------|
| Namespaces | 1-24 | XML namespaces for NF-e, NFS-e, MDF-e, CT-e, SOAP, signatures |
| XSD paths | 26-39 | Paths to XML Schema files |
| ICMS types | 42-84 | `ICMS_TIPOS_TRIBUTACAO` — all CST/CSOSN codes |
| ICMS origins | 86-123 | `ICMS_ORIGENS` — product origin codes (0-8) |
| ICMS modalities | 125-140 | `ICMS_MODALIDADES` and `ICMS_ST_MODALIDADES` |
| ICMS exemption reasons | 142-161 | `ICMS_MOTIVO_DESONERACAO` |
| NF-e status | 163-182 | `NF_STATUS` and `MDFE_STATUS` tuples |
| NF-e enumerations | 184-248 | Document types, emission processes, DANFE types, payment forms, emission forms, **emission purposes (incl. finNFe 5/6)**, referenced types, specific products, environments |
| IPI types | 250-270 | `IPI_TIPOS_TRIBUTACAO` and `IPI_TIPOS_CALCULO` |
| PIS types | 272-407 | `PIS_TIPOS_TRIBUTACAO` — all CST codes (01-99) |
| COFINS types | 411-548 | `COFINS_TIPOS_TRIBUTACAO` — all CST codes (01-99) |
| Reforma Tributaria | 550-582 | `IBS_CBS_TIPOS_TRIBUTACAO` (15 CSTs, 3-digit) and `IS_TIPOS_TRIBUTACAO` (7 CSTs, 2-digit) — IVA Dual (EC 132/2023) |
| Freight modalities | 584-591 | `MODALIDADES_FRETE` (0-9) |
| Process origins | 593-599 | `ORIGENS_PROCESSO` |
| State codes | 601-633 | `CODIGOS_ESTADOS` — UF to IBGE code mapping |
| Card brands | 635-664 | `BANDEIRA_CARTAO` (01-99) |
| Payment methods | 666-681 | `FORMAS_PAGAMENTO` (01-99) |

## Key Constants

| Constant | Value | Used For |
|----------|-------|----------|
| `NAMESPACE_NFE` | `http://www.portalfiscal.inf.br/nfe` | NF-e XML |
| `NAMESPACE_MDFE` | `http://www.portalfiscal.inf.br/mdfe` | MDF-e XML |
| `NAMESPACE_CTE` | `http://www.portalfiscal.inf.br/cte` | CT-e XML |
| `VERSAO_PADRAO` | `4.00` | NF-e version |
| `VERSAO_MDFE` | `3.00` | MDF-e version |
| `VERSAO_CTE` | `3.00` | CT-e version |
| `VERSAO_QRCODE` | `2` | QR Code version |
| `CODIGO_BRASIL` | `1058` | Country code for Brazil |
42 changes: 42 additions & 0 deletions docs/manifesto_map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Source Map: `manifesto.py` (447 lines)

Entity models for MDF-e (Manifesto de Documentos Fiscais Eletrônicos).

## Classes Overview

| Class | Lines | Purpose |
|-------|-------|---------|
| `Manifesto` | 12-200 | Main MDF-e entity |
| `ManifestoEmitente` | ~203-230 | MDF-e issuer |
| `ManifestoMunicipioCarrega` | ~232-240 | Loading municipality |
| `ManifestoPercurso` | ~242-248 | Route waypoint (UF) |
| `ManifestoModalRodoviario` | ~250-290 | Road transport modal |
| `ManifestoCIOT` | ~292-300 | CIOT (toll system) |
| `ManifestoPedagio` | ~302-315 | Toll information |
| `ManifestoContratante` | ~317-330 | Contractor info |
| `ManifestoVeiculoTracao` | ~332-360 | Traction vehicle |
| `ManifestoVeiculoReboque` | ~362-385 | Trailer vehicle |
| `ManifestoProprietario` | ~387-400 | Vehicle owner |
| `ManifestoCondutor` | ~402-410 | Driver |
| `ManifestoDocumentos` | ~412-425 | Linked documents (NF-e/CT-e) |
| `ManifestoSeguradora` | ~427-440 | Insurance |
| `ManifestoTotais` | ~442-447 | Totals (weight, cargo value) |

## `Manifesto` — Lines 12-200

### Field Groups
| Section | Lines | Fields |
|---------|-------|--------|
| Identity | 12-68 | uf, tipo_emitente, tipo_transportador, modelo (58), serie, numero_mdfe, codigo_numerico_aleatorio, modal, data_emissao, forma_emissao, processo_emissao, UFIni, UFFim |
| Read-only | 65-72 | digest_value, protocolo, data |
| Relationships | 74-110 | municipio_carrega, percurso, dhIniViagem, emitente, modal_rodoviario, documentos, seguradora, produto, totais, lacres, responsavel_tecnico |
| Additional info | ~112-120 | informacoes_adicionais_interesse_fisco, informacoes_complementares_interesse_contribuinte |

### Methods
| Method | Lines | Purpose |
|--------|-------|---------|
| `__init__()` | ~122-140 | Initialize all list fields |
| `adicionar_*()` | ~142-185 | Add municipio_carrega, percurso, emitente, modal_rodoviario, documento, seguradora, produto, totais, lacre, responsavel_tecnico |
| `_codigo_numerico_aleatorio()` | ~187-190 | Generate random 8-digit code |
| `_dv_codigo_numerico()` | ~192-200 | Calculate check digit (mod 11) |
| `identificador_unico` (property) | ~200+ | Build 44-char MDF-e access key |
Loading