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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "cat \"${CLAUDE_PROJECT_DIR}/AGENTS.md\""
}
]
}
]
}
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand Down
127 changes: 28 additions & 99 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,115 +1,44 @@
# AGENTS.md - PyNFe
# AGENTS.md PyNFe

Brazilian electronic fiscal document library (NF-e, NFC-e, NFS-e, MDF-e, CT-e) for SEFAZ webservice communication.
Brazilian electronic fiscal document library for NF-e, NFC-e, NFS-e, MDF-e, CT-e, and SEFAZ communication. `AGENTS.md` is the sole canonical instruction source; client settings may load it but must not copy its policies.

## Source Map Navigation (MANDATORY)
## Bootstrap and Git identity

**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
- Resolve the repository with `git rev-parse --show-toplevel`. For shared Nuvel resources, prefer a validated `NUVEL_WORKSPACE_ROOT`; otherwise derive candidates from the common Git directory/original checkout and accept only a directory containing `.claude/workflows/issue-orchestrator.js`. Never assume `../docs` works from a detached worktree.
- Commit with the machine's global Git identity exactly as configured. Never set or override `user.name`, `user.email`, signing settings, author/committer environment variables, or `.git/config`. Derive branch prefixes and paths; never hardcode a developer identity or home path.
- Preserve user changes. Use focused branches and squash PR merges. Verify official SEFAZ/provider specifications before changing external fiscal contracts.

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

### Available Source Maps
Before reading a mapped source file longer than 200 lines, read its map in `docs/` and then open only the relevant source range. The maps cover `serializacao.py`, `comunicacao.py`, `autorizador_nfse.py`, `notafiscal.py`, `manifesto.py`, `evento.py`, `flags.py`, `webservices.py`, and `utils/__init__.py`; use [`docs/README.md`](docs/README.md) to select one.

| 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) |
## Commands and full checks

### How to Use Source Maps
Use Python 3.9+ in an isolated environment.

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)
```bash
python -m pip install --upgrade pip
python -m pip install build -e . -r requirements.txt -r requirements-dev.txt -r requirements-nfse.txt
pytest -v
ruff check .
ruff format --check .
python -m build
git diff --check
```

## 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
`requirements-dev.txt` pins Ruff 0.12.5, matching the existing Poetry minimum; use that environment so lint results are reproducible.

## Commands
A focused `pytest tests/<file>.py` is useful during development but never replaces the full suite. Project metadata requires Python 3.9+, and CI tests every supported minor from 3.9 through 3.13; do not claim support outside that declared range or weaken it to accommodate an obsolete runner. Pushes and PRs run format, lint, and tests across the CI matrix. Every push also builds distributions; publishing occurs only for tags. Do not create a tag or publish without explicit authorization.

```bash
# Run tests
pytest tests/
## Architecture and invariants

# Run specific test
pytest tests/test_nfe_serializacao_geral.py

# Lint
ruff check pynfe/

# Format
ruff format pynfe/
```
- `pynfe/entidades/` contains fiscal domain entities; `pynfe/processamento/` owns signing, serialization, communication, and validation; `pynfe/utils/` contains flags, endpoint tables, XML helpers, and generated NFS-e bindings; `pynfe/data/` holds schemas and reference data.
- Never manually edit generated PyXB bindings under `pynfe/utils/nfse/`. Treat XSDs and reference tables as controlled fiscal inputs; change them only with verified source material.
- SEFAZ XML order, namespaces, decimal formatting, optional element rules, endpoint/environment selection, and signature/certificate handling are protocol contracts. Preserve them and add fixture-based tests for changed output.
- Extend existing entities, flags, serializers, and communication paths before creating parallel models or pipelines. Keep public APIs backward-compatible unless a breaking release is explicitly authorized.
- Tests must exercise real entity serialization/validation state. Do not mock core entity or XML behavior merely to simulate fiscal success or failure.
- Read [`docs/reforma_tributaria.md`](docs/reforma_tributaria.md) before changing IBS/CBS or tax-reform fields.

## 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`
Core dependencies include `lxml`, `signxml`, `cryptography`/`pyopenssl`, and `requests`; NFS-e additionally uses `suds-community` and `PyXB-X`. Do not casually broaden supported Python/dependency ranges because certificate and generated-binding compatibility is sensitive.
16 changes: 16 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Documentation index

| Document | What it covers |
|---|---|
| [reforma_tributaria.md](reforma_tributaria.md) | Brazilian tax reform support and fiscal serialization decisions. |
| [serializacao_map.md](serializacao_map.md) | Navigation map for NF-e, NFC-e, and MDF-e XML serialization. |
| [comunicacao_map.md](comunicacao_map.md) | Navigation map for SEFAZ communication. |
| [autorizador_nfse_map.md](autorizador_nfse_map.md) | Navigation map for municipal NFS-e authorization. |
| [notafiscal_map.md](notafiscal_map.md) | Navigation map for invoice entities and tax fields. |
| [manifesto_map.md](manifesto_map.md) | Navigation map for MDF-e entities. |
| [evento_map.md](evento_map.md) | Navigation map for fiscal event entities. |
| [flags_map.md](flags_map.md) | Navigation map for fiscal constants, namespaces, and codes. |
| [webservices_map.md](webservices_map.md) | Navigation map for endpoints by state and environment. |
| [utils_map.md](utils_map.md) | Navigation map for shared utility functions. |

Read the matching source map before opening any mapped source file longer than 200 lines. Repository-wide commands and invariants live in [`../AGENTS.md`](../AGENTS.md).
50 changes: 47 additions & 3 deletions docs/reforma_tributaria.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,43 @@ A implementacao cobre:
- `vNF` **NAO inclui** IBS/CBS (proibido em 2025-2026)
- `finNFe=5` (Nota de Debito) e `finNFe=6` (Nota de Credito)
- Campos de entidade para IS (Imposto Seletivo) — **armazenados mas nao serializados** ate o schema suportar (2027)
- Tributacao monofasica (`gIBSCBSMono`) para CST 620 — combustiveis e demais produtos sujeitos ao regime monofasico de IBS/CBS

**Nao inclui** (ainda): Split Payment, cashback, eventos de apuracao assistida, Grupo VB (total do item), Grupo VC (referenciamento de DF-e), Grupo BB (antecipacao de pagamento), tributacao monofasica (`gIBSCBSMono`), diferimento per-item (`gDif`), devolucao de tributos per-item (`gDevTrib`), reducao de aliquota per-item (`gRed`), estorno de credito (`gEstornoCred`), credito presumido per-item (`gCredPresOper`, `gCredPresIBSZFM`).
**Nao inclui** (ainda): Split Payment, cashback, eventos de apuracao assistida, Grupo VB (total do item), Grupo VC (referenciamento de DF-e), Grupo BB (antecipacao de pagamento), diferimento per-item (`gDif`), devolucao de tributos per-item (`gDevTrib`), reducao de aliquota per-item (`gRed`), estorno de credito (`gEstornoCred`), credito presumido per-item (`gCredPresOper`, `gCredPresIBSZFM`).

### Tributacao monofasica — `gIBSCBSMono`

Para produtos com CST 620 (combustiveis, etc.) o grupo emitido dentro de `<IBSCBS>` e `<gIBSCBSMono>` ao inves de `<gIBSCBS>`. Conforme o schema oficial (`DFeTiposBasicos_v1.00.xsd`, type `TMonofasia`), os cinco campos monofasicos vivem sob o wrapper obrigatorio `<gMonoPadrao>` e na ordem definida pelo schema. Alem disso, `<vTotIBSMonoItem>` e `<vTotCBSMonoItem>` sao SIBLINGS de `<gMonoPadrao>` (NAO filhos) e ambos sao OBRIGATORIOS por schema (sem `minOccurs=0`):

```xml
<gIBSCBSMono>
<gMonoPadrao>
<qBCMono>18.0000</qBCMono>
<adRemIBS>0.1000</adRemIBS>
<adRemCBS>0.0000</adRemCBS>
<vIBSMono>1.80</vIBSMono>
<vCBSMono>0.00</vCBSMono>
</gMonoPadrao>
<vTotIBSMonoItem>1.80</vTotIBSMonoItem>
<vTotCBSMonoItem>0.00</vTotCBSMonoItem>
</gIBSCBSMono>
```

| Campo | Tipo | Descricao |
|-------|------|-----------|
| `qBCMono` | TDec1104RTC | Quantidade tributada na base monofasica |
| `adRemIBS` | TDec_0302_04RTC | Aliquota ad rem IBS (valor em BRL por unidade) |
| `adRemCBS` | TDec_0302_04RTC | Aliquota ad rem CBS (valor em BRL por unidade) |
| `vIBSMono` | TDec1302RTC | Valor IBS monofasico |
| `vCBSMono` | TDec1302RTC | Valor CBS monofasico |
| `vTotIBSMonoItem` | TDec1302RTC | Total IBS monofasico do item (sibling de `gMonoPadrao`) |
| `vTotCBSMonoItem` | TDec1302RTC | Total CBS monofasico do item (sibling de `gMonoPadrao`) |

Atributos na entidade `NotaFiscalProduto`: `ibscbs_q_bc_mono`, `ibscbs_ad_rem_ibs`, `ibscbs_v_ibs_mono`, `ibscbs_ad_rem_cbs`, `ibscbs_v_cbs_mono`, `ibscbs_v_tot_ibs_mono_item`, `ibscbs_v_tot_cbs_mono_item`.

Para itens single-line (sem retencao / retencao anterior / diferimento), `vTotIBSMonoItem == vIBSMono` e `vTotCBSMonoItem == vCBSMono`. Quando os atributos nao sao informados pelo caller, o serializador emite `0.00` (default seguro durante o Teste de Carga 2026 com ad rem zerados).

Durante o Teste de Carga 2026 os ad rem ainda nao foram publicados pela SEFAZ, entao os valores podem ser zerados — o grupo `gIBSCBSMono` ainda sera emitido corretamente.

## CSTs disponiveis

Expand Down Expand Up @@ -300,13 +335,22 @@ Os totais ficam em um grupo **separado** de `<ICMSTot>`, como irmao dentro de `<
<vCredPres>0.00</vCredPres>
<vCredPresCondSus>0.00</vCredPresCondSus>
</gCBS>
<!-- gMono: totais monofasia (nao implementado) -->
<gMono> <!-- Totais monofasia (DEV-1955) -->
<vIBSMono>0.00</vIBSMono> <!-- Total IBS monofasico padrao -->
<vCBSMono>0.00</vCBSMono> <!-- Total CBS monofasica padrao -->
<vIBSMonoReten>0.00</vIBSMonoReten> <!-- Total IBS monofasico sujeito a retencao -->
<vCBSMonoReten>0.00</vCBSMonoReten> <!-- Total CBS monofasica sujeita a retencao -->
<vIBSMonoRet>0.00</vIBSMonoRet> <!-- Total IBS monofasico retido anteriormente -->
<vCBSMonoRet>0.00</vCBSMonoRet> <!-- Total CBS monofasica retida anteriormente -->
</gMono>
<!-- gEstornoCred: totais estorno de credito (nao implementado) -->
</IBSCBSTot>
</total>
```

> Os subgrupos `gIBS` e `gCBS` sao opcionais (`minOccurs="0"`) — emitidos apenas quando ha valores. Os campos `vDif`, `vDevTrib`, `vCredPres` e `vCredPresCondSus` sao obrigatorios dentro de cada subgrupo (emitidos como "0.00" quando nao utilizados).
>
> O subgrupo `gMono` e opcional, mas obrigatorio sempre que algum item da NF-e carregar `<gIBSCBSMono>` (CST 620, etc.). Quando emitido, **todos os seis filhos** sao obrigatorios (`vIBSMono`, `vCBSMono`, `vIBSMonoReten`, `vCBSMonoReten`, `vIBSMonoRet`, `vCBSMonoRet`). Omitir `<gMono>` em uma NF-e com items monofasicos faz a SEFAZ rejeitar com `cStat 1119 - "Total de IBS e CBS nao informado"`. Os totais `Reten`/`Ret` ainda nao sao acumulados a nivel de item (PyNFe ainda so emite `<gMonoPadrao>`), entao serao "0.00" ate que `<gMonoReten>` / `<gMonoRet>` / `<gMonoDif>` sejam suportados a nivel de item.

### Cabecalho — `cMunFGIBS` no `<ide>`

Expand Down Expand Up @@ -338,7 +382,7 @@ Esses CSTs geram apenas `<CST>` e `<cClassTrib>`, sem `<gIBSCBS>`.

- **`cClassTrib`**: Emitido quando informado (campo obrigatorio na pratica)
- **`cMunFGIBS`**: Emitido no `<ide>` apenas quando informado
- **`<IBSCBSTot>`**: Tipo `TIBSCBSMonoTot`. Omitido se todos os totais forem zero. Quando emitido, `vBCIBSCBS` e obrigatorio como primeiro filho; `gIBS` e `gCBS` sao opcionais
- **`<IBSCBSTot>`**: Tipo `TIBSCBSMonoTot`. Omitido se todos os totais forem zero E nenhum item carregar `<gIBSCBSMono>`. Quando emitido, `vBCIBSCBS` e obrigatorio como primeiro filho; `gIBS`, `gCBS` e `gMono` sao opcionais (mas `gMono` e obrigatorio sempre que houver items monofasicos)
- **`<IBSCBS>`**: Tipo `TTribNFe`. Omitido completamente se `ibscbs_cst` nao for informado
- **IS (`<IS>`)**: Tipo `TIS`. **Nao emitido no XML** — serializacao desabilitada ate 2027
- **`<ISTot>`**: Tipo `TISTot`. **Nao emitido** — sera irmao de `<IBSCBSTot>` (antes dele no schema)
Expand Down
30 changes: 16 additions & 14 deletions docs/serializacao_map.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# Source Map: `serializacao.py` (2771 lines)
# Source Map: `serializacao.py` (2881 lines)

XML serialization of NF-e, NFC-e, NFS-e and MDF-e documents into SEFAZ-compliant XML format.

## Classes Overview

| Class | Lines | Purpose |
|-------|-------|---------|
| `Serializacao` | 30-63 | Abstract base class (not instantiable directly) |
| `SerializacaoXML` | 66-1860 | Main NF-e/NFC-e XML serialization |
| `SerializacaoQrcode` | 2102-2206 | NFC-e QR Code generation |
| `SerializacaoNfse` | 2209-2275 | NFS-e serialization (Betha/Ginfes) |
| `SerializacaoQrcodeMDFe` | 2278-2301 | MDF-e QR Code generation |
| `SerializacaoMDFe` | 2304-2771 | MDF-e XML serialization |
| `Serializacao` | 31-65 | Abstract base class (not instantiable directly) |
| `SerializacaoXML` | 67-2222 | Main NF-e/NFC-e XML serialization |
| `SerializacaoQrcode` | 2225-2309 | NFC-e QR Code generation |
| `SerializacaoNfse` | 2312-2378 | NFS-e serialization (Betha/Ginfes) |
| `SerializacaoQrcodeMDFe` | 2381-2404 | MDF-e QR Code generation |
| `SerializacaoMDFe` | 2407-2881 | MDF-e XML serialization |

---

## `Serializacao` (base class) — Lines 30-63
## `Serializacao` (base class) — Lines 31-65

Abstract base for all serializers. Stores `_fonte_dados`, `_ambiente` (1=prod, 2=homolog), `_contingencia`, `_so_cpf`.

## `SerializacaoXML` — Lines 66-1860
## `SerializacaoXML` — Lines 67-2222

### Exported Methods
| Method | Lines | Purpose |
Expand Down Expand Up @@ -102,19 +102,21 @@ Abstract base for all serializers. Stores `_fonte_dados`, `_ambiente` (1=prod, 2

---

## `SerializacaoQrcode` — Lines 2102-2206
## `SerializacaoQrcode` — Lines 2225-2309

Generates NFC-e QR Code URL. Handles online/offline modes and state-specific URL patterns (SP, BA, MG, etc.).
Generates the `<infNFeSupl>` block. `<qrCode>` handles online/offline modes and the
state-specific host/path patterns; `<urlChave>` is a separate registry and comes straight from
`webservices.url_consulta_chave` — never concatenate a host onto it (DEV-2468, rejeicao 878).

## `SerializacaoNfse` — Lines 2209-2275
## `SerializacaoNfse` — Lines 2312-2378

Delegates to Betha or Ginfes serializers. Methods: `gerar`, `gerar_lote`, `consultar_nfse`, `consultar_lote`, `consultar_rps`, `consultar_situacao_lote`, `cancelar`.

## `SerializacaoQrcodeMDFe` — Lines 2278-2301
## `SerializacaoQrcodeMDFe` — Lines 2381-2404

Generates MDF-e QR Code URL using SVRS endpoint.

## `SerializacaoMDFe` — Lines 2304-2771
## `SerializacaoMDFe` — Lines 2407-2881

### Methods
| Method | Lines | Purpose |
Expand Down
Loading
Loading