Metadata-Version: 2.4
Name: tenzan-nfe-python-sdk
Version: 2.0.1
Summary: SDK Python para integração com a API pública do saas-core (NF-e, NFC-e, CT-e, MDF-e, NFCom, DC-e, NFS-e)
Author: Tenzan Labs
License: ISC
Project-URL: Homepage, https://github.com/Tenzan-Labs/nfe-sdks/tree/main/python#readme
Project-URL: Repository, https://github.com/Tenzan-Labs/nfe-sdks
Project-URL: Issues, https://github.com/Tenzan-Labs/nfe-sdks/issues
Keywords: nfe,nfce,nfse,cte,mdfe,sdk,fiscal
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: ISC License (ISCL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.31.0

# tenzan-nfe-python-sdk

SDK oficial (Python) para a API pública do saas-core.

## Instalação

```bash
pip install tenzan-nfe-python-sdk
```

## Uso

```python
from nfe_sdk import NFeClient

client = NFeClient("nfe_access_xxxxxxxx", "nfe_test_xxxxxxxx")  # ou passe base_url como 3º argumento

res = client.emit({
    "mod": "55",
    "natureza_operacao": "Venda de mercadoria",
    # ...demais campos do documento (ver nfe_sdk/document_types.py)
})

# emit/cancel/correct são assíncronos (processados via fila) — faça polling em get_invoice
invoice = client.get_invoice("55", res["invoiceId"])
```

Cada tipo de documento (NF-e, NFC-e, CT-e, MDF-e, NFCom, DC-e, NFS-e) tem seu próprio
namespace de rota na API (`/v1/nfe`, `/v1/nfc-e`, `/v1/cte`, ...). `emit()` escolhe o
namespace certo a partir do `mod` do payload; os demais métodos que recebem um
`invoice_id` (`cancel`, `get_invoice`, `check_status`, `list_voided_ranges`, etc.) recebem
`mod` como primeiro argumento para o mesmo fim — é o modelo do documento sendo
operado, não necessariamente o mesmo `mod` usado na emissão original de outro tipo.

## Autenticação

Duas credenciais, ambas obrigatórias:

- `X-Access-Key: <access_key>` (`nfe_access_...`) — identifica a **conta** que está integrando.
- `Authorization: Bearer <api_key>` (`nfe_live_...` em produção, `nfe_test_...` em homologação — o ambiente é definido pelo prefixo da própria chave) — identifica **qual tenant** da conta usar.

Uma sem a outra não autentica. Ambas são geradas no painel do saas-core. O SDK **nunca** envia certificado digital — isso é resolvido no saas-core a partir da API key.

`base_url` (3º argumento do construtor) tem fallback em `NFE_SAAS_URL`, default `https://app.nfe.tenzanlabs.com`.

## Tipos por documento fiscal

Os payloads de `emit()` são tipados por `mod` (`NfeData`, `CteData`, `MdfeData`, `NfcomData`, `DceData`, `NfseData`, em `nfe_sdk/document_types.py`) e são **gerados automaticamente** a partir dos schemas Zod do motor fiscal — não edite esses arquivos à mão, regenere-os quando o schema do backend mudar.

## Erros

Todo erro HTTP vira `NFeApiError` (`http_status`, `message` normalizada, `body` cru).

## Gestão de clientes (`ClientsClient`)

Cadastro/consulta de clientes (tenants) da conta é uma API separada (`/v1/clients`), com um modelo de credencial diferente — não usa `NFeClient`:

```python
from nfe_sdk import ClientsClient

# Só a Access Key — essa API não opera sobre um tenant específico
clients = ClientsClient("nfe_access_xxxxxxxx")

cliente = clients.create_client({
    "name": "Loja X",
    "cnpj": "12345678000199",
    "ie": "123456789",
    "uf": "SP",
    "csc": "...",
    "cscId": "1",
    "certificateBase64": "...",  # .pfx/.p12 em base64
    "certificatePassword": "...",
})

todos = clients.list_clients()  # [{ "id", "name", "cnpj" }, ...]
```

`get_tokens_by_cnpj` (devolve os tokens `nfe_test_.../nfe_live_...` sem máscara) exige a Chave Restrita — passe `restricted_key` no construtor:

```python
clients = ClientsClient("nfe_access_xxxxxxxx", restricted_key="nfe_restricted_xxxxxxxx")
tokens = clients.get_tokens_by_cnpj("12345678000199")["tokens"]
```

Chamar `get_tokens_by_cnpj` sem `restricted_key` lança erro local antes de chamar a API.

## Desenvolvimento

```bash
python -m unittest discover -s tests
```
