Metadata-Version: 2.5
Name: shade-plugins-integra-contador
Version: 0.1.0
Summary: Situação fiscal, Caixa Postal, DAS do Simples e procurações pela API oficial Integra Contador do SERPRO, com o contrato e o e-CNPJ do escritório.
Author: ShadeOne
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: cryptography>=44
Requires-Dist: shade-sdk<0.23,>=0.22.8
Description-Content-Type: text/markdown

# shade-plugins-integra-contador

Consulta a situação fiscal e a Caixa Postal, gera o DAS do Simples e lista as procurações de cada
cliente pela API oficial Integra Contador do SERPRO, de dentro de um robô do
[`shade-sdk`](https://pypi.org/project/shade-sdk/). O contrato e o e-CNPJ são os do escritório, e a
cobrança do SERPRO é direto com ele. Sem tela e sem captcha: a Receita responde pela API.

O escritório contrata a Integra Contador na [Loja SERPRO](https://loja.serpro.gov.br/integracontador)
e recebe a consumer key e o secret. Cada cliente alcança o escritório pela procuração eletrônica que
deu no e-CAC. No `shade.toml` do robô:

```toml
[hosts]
permitidos = ["autenticacao.sapi.serpro.gov.br", "gateway.apiserpro.serpro.gov.br"]

[credenciais]
nomes = ["INTEGRA_CONTADOR_CONSUMER_KEY", "INTEGRA_CONTADOR_CONSUMER_SECRET"]

[certificado]
modo = "fixo"
nome = "Escritório"   # o nome do e-CNPJ no cofre, com autenticacao.sapi.serpro.gov.br nos hosts dele

[config]
integra_contador_cnpj = "12345678000190"   # o CNPJ do contrato

[plugins]
integra-contador = "0.1.0"
```

```python
from pathlib import Path

import shade_plugins_integra_contador as integra

# O PDF do relatório de situação fiscal: espera o tempo que o SERPRO informa.
pdf = integra.situacao_fiscal(item["cnpj"])
Path("situacao.pdf").write_bytes(pdf)
item.anexar("situacao.pdf")

# As mensagens a partir de uma data, com o conteúdo de cada uma.
for mensagem in integra.caixa_postal(item["cnpj"], "2026-10-01"):
    print(mensagem["data_envio"], mensagem["assunto"])

# A guia do DAS: gerar não paga nem declara.
guia = integra.das_simples(item["cnpj"], "2026-10")
Path("das.pdf").write_bytes(guia["pdf"])
item.saida(vencimento=guia["vencimento"], total=str(guia["valores"]["total"]))

# As procurações que o cliente deu ao escritório.
for procuracao in integra.procuracoes(item["cnpj"]):
    print(procuracao["expira_em"], procuracao["sistemas"])
```

## As operações

| Operação | Devolve | Serviço do SERPRO |
|---|---|---|
| `situacao_fiscal(cnpj)` | os bytes do PDF | SITFIS: protocolo e relatório |
| `caixa_postal(cnpj, desde=None, *, nao_lidas=False, conteudo=True, limite=100)` | lista de mensagens, da mais nova para a mais antiga | Caixa Postal: lista e detalhe |
| `das_simples(cnpj, competencia, *, consolidar_em=None)` | `pdf`, `numero_documento`, `competencia`, `vencimento`, `limite_acolhimento`, `valores` (`Decimal`), `observacoes` | PGDAS-D: gerar DAS |
| `procuracoes(cnpj)` | lista com `expira_em` e `sistemas` | Procurações |

A linha digitável do DAS não vem na resposta da API: ela está no PDF, e o
[`shade-plugins-documentos-fiscais`](https://pypi.org/project/shade-plugins-documentos-fiscais/)
confere o boleto.

- **Só leitura:** nenhuma operação declara nem paga nada, então todas rodam também no ensaio.
- **Custo:** cada consulta conta na fatura do SERPRO. Em `caixa_postal`, cada `conteudo` é uma
  consulta a mais; `conteudo=False` fica só com a lista. `situacao_fiscal` espera o tempo informado
  antes de pedir o relatório, porque a resposta "ainda em processamento" também é cobrada.
- **Caixa Postal cheia:** a lista traz as mais recentes, até `limite`; `.completa` é `False` se sobrou
  mensagem do período. A mensagem cujo conteúdo não coube no prazo volta com `conteudo` `None`.
- **Prazo:** `timeout=` (120 s) vale para a chamada inteira, espera e retentativas incluídas.

## Os erros

| Erro | Quando | O que o robô faz |
|---|---|---|
| `IntegraIndisponivel` | SERPRO fora do ar, sem resposta, limite de pedidos, resposta fora da forma documentada, relatório que não ficou pronto no prazo | o laço repete o item, pela política de retentativa do robô |
| `RecusaDoIntegra` (`ErroDeNegocio`, com `motivo`, `status` e `codigo`) | `sem_procuracao`, `contribuinte_invalido`, `servico_nao_contratado`, `contrato_de_outro_cnpj`, `certificado_recusado`, `autenticacao_recusada`, `acesso_negado`, `entrada_invalida`, `nao_encontrado` | o item falha com o motivo, sem repetir |
| `CredencialRecusada` | o SERPRO recusou a consumer key ou o secret | o lote para: confira na Loja SERPRO |
| `SemCertificado` | o e-CNPJ não veio do cofre, venceu ou não inclui o host do login | o item fica retido |

## Segurança

- O e-CNPJ chega em memória e nunca vai a disco. Ele só é apresentado no login, ao host de
  autenticação do SERPRO.
- A consumer key, o secret e os tokens não aparecem em evento, mensagem de erro nem exceção.
- O plugin fala só com os dois hosts do SERPRO e não segue redirecionamento.
- A mensagem de erro traz o código do SERPRO, nunca o texto dele, que costuma citar o CNPJ.

Licença Apache-2.0.
