Skip to main content

Biblioteca para automatização web nos sistemas do Tesouro do Estado do Rio de Janeiro

Project description

🪐 jupiter-subtes

jupiter-subtes é uma biblioteca Python publicada no PyPI para automatizar tarefas rotineiras do setor contábil do Tesouro do Estado do Rio de Janeiro. Ela unifica, em um único pacote, três domínios de automação: o sistema contábil SIAFE-Rio2, o sistema de documentos SEI e a integração com o ecossistema Microsoft 365 (SharePoint e Graph API).

pip install jupiter-subtes

Visão Geral dos Módulos

Módulo Classe principal Domínio
siafelibrary Siafe Automação web do SIAFE-Rio2 (GR, PDE, PDT, NP, NA, EV)
seilibrary SEI Automação web do SEI (processos, documentos, despachos)
apipoint SharePoint, GraphAPI SharePoint (arquivos/pastas) e Microsoft Graph API
from jupiter import Siafe, SEI, SharePoint, GraphAPI

1. siafelibrary — Automação do SIAFE

A classe Siafe encapsula toda a interação com o SIAFE-Rio2. Ela gerencia o navegador automaticamente (herda de automaweb.Navegador) e expõe métodos de alto nível para login, geração de documentos em lote e consultas.

Documentos suportados

Método Tipo de documento
gerar_GR Guia de Recolhimento (orçamentária e extra-orçamentária)
gerar_PDE Programação de Desembolso Extra-orçamentária
gerar_PDT Programação de Desembolso de Transferência
gerar_NP Nota Patrimonial
gerar_NA Nota de Aplicação e Resgate
gerar_EV Nota de Evento

Exemplo completo: geração de GRs em lote

import pandas as pd
from jupiter import Siafe

# 1. DataFrame com os lançamentos pendentes
#    Colunas obrigatórias: id, data, valor, observacao, tipo_id
#    Colunas preenchidas pelo robô: num_documento, tempo_contab
df = pd.DataFrame([
    {"id": 1, "data": "01/07/2025", "valor": 1500.00,
     "observacao": "Recolhimento referente a julho/2025",
     "tipo_id": "GR_FUNDO_A", "num_documento": None, "tempo_contab": None},
    {"id": 2, "data": "01/07/2025", "valor": 3200.50,
     "observacao": "Recolhimento referente a julho/2025",
     "tipo_id": "GR_FUNDO_B", "num_documento": None, "tempo_contab": None},
])

# 2. Dicionário de regras contábeis (um por tipo_id)
regras = {
    "GR_FUNDO_A": {
        "TipoDocumento":            "01 - Arrecadação",
        "UG":                       "123456",
        "DomicilioBancario":        "0001",
        "DomicilioBancarioCompleto":"0001 - BANCO DO BRASIL S.A.",
        "ExtraOrcamentario":        False,
        "IEF":                      "1",
        "Fonte":                    "100",
        "FonteRJ":                  "100",
        "TipoDetalhamentoFonte":    "0",
        "DetalhamentoFonte":        "0000",
        "Convenio":                 "99999",
        "TipoPatrimonial":          "Ativo",
        "ItemPatrimonial":          "Bancos Conta Movimento",
        "OperacaoPatrimonial":      "Entrada de Recursos",
        "NaturezaReceita":          "11180111",
    },
    "GR_FUNDO_B": {
        # ... mesma estrutura, campos diferentes
    },
}

# 3. Callback chamado a cada documento gerado com sucesso
def ao_contabilizar(id, num_documento, tempo_contab):
    print(f"[OK] id={id}  doc={num_documento}  tempo={tempo_contab}s")
    # Aqui você pode salvar no banco de dados, enviar e-mail, etc.

# 4. Execução
siafe = Siafe()
if siafe.logar_siafe(versaoSiafe=1, usuario="12345678900", senha="senha"):
    siafe.gerar_documento(
        funcao=siafe.gerar_GR,
        df=df,
        dict_map=regras,
        callback_sucesso=ao_contabilizar,
    )

Exemplo: login e consulta via API REST

from jupiter import Siafe

siafe = Siafe()
siafe.logar_siafe_API(versaoSiafe=1, usuario="12345678900", senha="senha")

# Consulta um relatório do FlexVision pelo ID
df_resultado = siafe.consulta_flexvision(
    id="12345",
    parametros_consulta="2025,123456"  # parâmetros separados por vírgula
)
print(df_resultado.head())

Exemplo: consultar e imprimir GRs por número

# Após gerar_documento, df['num_documento'] já está preenchido.
# Este método abre cada GR no SIAFE e salva o PDF automaticamente.
siafe.consultar_GR_numDoc(df=df, callback_sucesso=lambda id: print(f"PDF gerado para id={id}"))

Ambientes disponíveis (versaoSiafe)

Valor Ambiente
1 Produção
2 Beta / Testes
3 Homologação
4 SIAFE-Rio 1 (legado)

2. seilibrary — Automação do SEI

A classe SEI automatiza o Sistema Eletrônico de Informações (SEI). Ela também herda de automaweb.Navegador e oferece métodos para navegar em processos, analisar documentos, incluir despachos e gerenciar blocos de assinatura.

Exemplo: controle de processos da unidade

from jupiter import SEI

sei = SEI()
sei.logar_sei(usuario="joao.silva", senha="senha", orgao="SEFAZ")
sei.trocar_unidade("SUPCONC")

# Retorna todos os processos visíveis no Controle de Processos
# com todas as colunas (tipo, atribuicao, marcadores, prazo, etc.)
processos = sei.controlar_processos()

for p in processos:
    print(p["processo"], "|", p["tipo"], "|", p["marcadores"])

Exemplo: filtrar processos por marcador e incluir despacho

from jupiter import SEI

sei = SEI()
sei.logar_sei(usuario="joao.silva", senha="senha", orgao="SEFAZ")

# Obtém os processos da visualização por marcador
todos = sei.visualizar_processos_por_marcador(marcador="Aguardando Despacho")

# Filtra apenas os que têm o marcador desejado
pendentes = sei.filtrar_processos_por_marcador(todos, marcador="Aguardando Despacho")

for numero in pendentes:
    sei.pesquisar_processo(numero)
    sei.expandir_pastas()

    # Analisa os documentos do processo
    documentos = sei.analisar_documentos(condicao="Memorando")

    # Verifica se algum memorando menciona os termos relevantes
    encontrados = sei.verificar_despacho(documentos, termos_busca=["aprovado", "autorizado"])

    if encontrados:
        # Cria um despacho de encaminhamento
        criado = sei.incluir_despacho(texto_padrao="Encaminhamento Padrão SUPCONC")
        if criado:
            sei.incluir_processo_bloco(bloco="Bloco Assinatura Mensal")

Exemplo: análise detalhada de documentos

sei.pesquisar_processo("0012345-67.2025.8.19.0000")
sei.expandir_pastas()

documentos = sei.analisar_documentos()

# verificar_despacho     → OR  (qualquer um dos termos)
# verificar_despacho_estrito  → AND (todos os termos)
# verificar_despacho_detalhado → quais termos estão em quais documentos

# Quero processos que mencionem "pagamento" E "autorizado"
com_ambos = sei.verificar_despacho_estrito(
    documentos,
    termos_busca=["pagamento", "autorizado"]
)

# Quero saber quais termos foram encontrados em cada documento
mapa = sei.verificar_despacho_detalhado(
    documentos,
    termos_busca=["pagamento", "autorizado", "pendente", "urgente"]
)
# Resultado: {"Memorando 001/2025": ["PAGAMENTO", "AUTORIZADO"], ...}
for doc, termos in mapa.items():
    print(f"{doc}: {termos}")

Exemplo: incluir anexo e formatar despacho

sei.pesquisar_processo("0012345-67.2025.8.19.0000")

# Inclui um arquivo externo (PDF, planilha, etc.) como Anexo
sei.incluir_anexo(
    nome_arvore="Balancete Julho 2025",
    caminho_arquivo="C:/Users/joao/Downloads/balancete_jul25.pdf",
)

# Inclui um despacho de encaminhamento pelo texto padrão cadastrado no SEI
sei.incluir_despacho(texto_padrao="Encaminhamento SUPCONC")

# Formata o despacho (substitui variáveis no editor CKEditor5 do SEI)
numero_gr = sei.copiar_informacoes_documento()  # obtém número do último documento
sei.formatar_despacho(
    titulo="Senhor Coordenador,",
    valor="1.500,00",
    valor_por_extenso="um mil e quinhentos reais",
    data="01/07/2025",
    num_documento=numero_gr,
    index_doc=numero_gr,
)

3. apipoint — Integração com SharePoint e Microsoft 365

SharePoint

A classe SharePoint gerencia arquivos e pastas em um site SharePoint corporativo. A autenticação usa cookies do navegador extraídos automaticamente via automaweb.

from jupiter import SharePoint

sp = SharePoint(site_url="https://tenant.sharepoint.com/sites/SetorContabil")

# Download de um arquivo específico
sp.download_arquivo(
    caminho_sharepoint="/sites/SetorContabil/Shared Documents/Relatorios/balancete.xlsx",
    pasta_local="C:/Users/joao/Downloads",
)

# Upload de um arquivo
sp.upload_arquivo(
    caminho_local="C:/Users/joao/Downloads/resultado_julho.xlsx",
    pasta_sharepoint="/sites/SetorContabil/Shared Documents/Resultados",
)

# Download recursivo de uma pasta inteira
sp.download_pasta(
    pasta_sharepoint="/sites/SetorContabil/Shared Documents/Backup",
    pasta_local="C:/Users/joao/Backup_SP",
)

# Verificar existência antes de sobrescrever
caminho = "/sites/SetorContabil/Shared Documents/Resultados/resultado_julho.xlsx"
if not sp.existe_arquivo(caminho):
    sp.upload_arquivo("C:/Users/joao/Downloads/resultado_julho.xlsx",
                      "/sites/SetorContabil/Shared Documents/Resultados")

# Criar estrutura de pastas e limpar arquivos antigos
sp.criar_pasta("/sites/SetorContabil/Shared Documents/2025/Julho")
sp.excluir_arquivo("/sites/SetorContabil/Shared Documents/2025/Junho/temp.xlsx")

GraphAPI (Teams e Outlook)

A classe GraphAPI usa autenticação app-only (client credentials) para integrar com Microsoft Teams e Outlook. Requer autorização prévia da TI.

from jupiter import GraphAPI

graph = GraphAPI(
    tenant_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    client_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    client_secret="seu-segredo-aqui",
    conta_coorporativa="automacao@sefaz.rj.gov.br",
)

# Enviar e-mail
graph.enviar_email(
    titulo="Rotina de Julho concluída",
    mensagem="A contabilização de 47 GRs foi concluída com sucesso.",
    destinatarios=["joao.silva@sefaz.rj.gov.br", "chefia@sefaz.rj.gov.br"],
)

# Listar e-mails não lidos
mensagens = graph.listar_emails(pasta="Inbox", top=5)

# Responder a um e-mail
graph.responder_email(
    id_mensagem="AAMkAGI...",
    texto_resposta="Confirmamos o recebimento. Aguarde processamento.",
)

# Enviar alerta via Teams (webhook)
graph.enviar_mensagem(
    webhook_url="https://sefaz.webhook.office.com/webhookb2/...",
    mensagem="Contabilizacao de julho finalizada.",
    destinatarios=["equipe-contabil"],
)

Arquivos XPath (_xpaths.py)

Os módulos siafelibrary_xpaths.py e seilibrary_xpaths.py centralizam os seletores XPath de cada tela do SIAFE e do SEI. São usados internamente pelas classes Siafe e SEI, mas podem ser acessados diretamente para estender a biblioteca ou inspecionar seletores.

Por que XPaths separados?

Quando a interface do SIAFE ou do SEI muda (atualização de sistema), basta atualizar o arquivo _xpaths.py correspondente — o código de lógica de negócio não precisa ser tocado.

Estrutura dos arquivos XPath

siafelibrary_xpaths.py — organizado por tela do SIAFE:

Classe Tela
siafe_xpaths_login Tela de login
xpaths_menu Menu principal de navegação
xpaths_gr Guia de Recolhimento
xpaths_pde PD Extra-orçamentária
xpaths_pdt PD de Transferência
xpaths_np Nota Patrimonial
xpaths_na Nota de Aplicação e Resgate
xpaths_ev Nota de Evento
xpaths_consulta Filtros de consulta de GR

seilibrary_xpaths.py — organizado por área do SEI:

Classe Área
sei_xpaths_login Tela de login
xpaths_pagina_inicial Barra de pesquisa e menu da unidade
xpaths_processos Árvore de documentos e iframes
xpaths_documento Formulário de inclusão de documento
xpaths_controle_processos Tabela do Controle de Processos

Como acessar

# Importação via pacote jupiter (conforme __init__.py)
from jupiter import xpaths_gr, siafe_xpaths_login, sei_xpaths_login

print(xpaths_gr.btn_inserir_gr)     # '//*[@id="pt1:tblGuiaRecolhimento:btnInsert"]'
print(siafe_xpaths_login.usuario)   # '//*[@id="loginBox:itxUsuario::content"]'
print(sei_xpaths_login.btn_acessar) # '//*[@id="sbmAcessar"]'

# Importação direta dos módulos (para acesso completo)
from jupiter.siafelibrary_xpaths import xpaths_login  # nome original sem alias
from jupiter.seilibrary_xpaths import xpaths_login

Exemplo: estendendo a biblioteca com um novo comportamento

from jupiter import Siafe, xpaths_gr

class SiafeCustom(Siafe):
    def verificar_numero_gr(self) -> str | None:
        """Retorna o número da GR exibido na tela atual."""
        if self.verifica_visivel(xpaths_gr.numero_documento):
            return self.obter_texto(xpaths_gr.numero_documento)
        return None

Estruturando o dict_map

O dict_map é o mecanismo central de separação entre regras de negócio e automação. Cada chave corresponde ao valor da coluna tipo_id no seu DataFrame; o valor é um dicionário com os campos que o SIAFE exige para aquele tipo de documento.

Referência completa das chaves

regras = {

    # ── GR Orçamentária ────────────────────────────────────────────────────
    "GR_ORC": {
        "TipoDocumento":             "01 - Arrecadação",        # texto exato do dropdown
        "UG":                        "123456",                   # código da UG emitente
        "DomicilioBancario":         "0001",                     # código para pesquisa
        "DomicilioBancarioCompleto": "0001 - BANCO DO BRASIL",   # texto para validação
        "ExtraOrcamentario":         False,

        "IEF":                       "1",
        "Fonte":                     "100",
        "FonteRJ":                   "100",
        "TipoDetalhamentoFonte":     "0",
        "DetalhamentoFonte":         "0000",   # opcional
        "Convenio":                  "99999",

        "TipoPatrimonial":           "Ativo",
        "ItemPatrimonial":           "Bancos Conta Movimento",
        "OperacaoPatrimonial":       "Entrada de Recursos",
        "NaturezaReceita":           "11180111",  # exclusivo: GR orçamentária
    },

    # ── GR Extra-orçamentária ──────────────────────────────────────────────
    "GR_EXTRA": {
        "TipoDocumento":             "02 - Extra-orçamentário",
        "UG":                        "654321",
        "DomicilioBancario":         "0002",
        "DomicilioBancarioCompleto": "0002 - CAIXA ECONÔMICA FEDERAL",
        "ExtraOrcamentario":         True,      # ativa o caminho extra-orçamentário

        "IEF": "1", "Fonte": "100", "FonteRJ": "100",
        "TipoDetalhamentoFonte": "0", "Convenio": "99999",

        "TipoPatrimonial":           "Passivo",
        "ItemPatrimonial":           "Obrigações a Pagar",
        "OperacaoPatrimonial":       "Saída de Recursos",

        "TipoCredor":                "PJ",          # PJ | PF | CG | UG
        "Credor":                    "12345678000199",
    },

    # ── PDT (Programação de Desembolso de Transferência) ───────────────────
    "PDT_01": {
        "UG":                           "123456",
        "UGFavorecida":                 "654321",  # opcional; padrão = UG
        "DomicilioBancarioOrigem":      "0001",
        "DomicilioBancarioOrigemCompleto": "0001 - BANCO DO BRASIL",
        "DomicilioBancarioDestino":     "0002",
        "DomicilioBancarioDestinoCompleto": "0002 - CAIXA",

        "IEF": "1", "Fonte": "100", "FonteRJ": "100",
        "TipoDetalhamentoFonte": "0", "DetalhamentoFonte": "0000",
        "Convenio": "99999",

        "TipoPatrimonial":              "Ativo",
        "ItemPatrimonial":              "Transferências",
        "OperacaoPatrimonial":          "899991",
        "SelecaoPorValor":              True,   # seleciona pelo código, não pelo texto

        "Regularizacao":                "01 - Tipo A",  # opcional: PDT de regularização
        "JustificativaRegularizacao":   "Regularização do mês anterior",  # opcional
    },

    # ── NP com Inscrição Genérica ──────────────────────────────────────────
    "NP_IG": {
        "UG":                        "123456",
        "TipoPatrimonial":           "Ativo",
        "ItemPatrimonial":           "Ajustes Patrimoniais",
        "OperacaoPatrimonial":       "Bloqueio",
        "SelecaoPorValor":           True,

        "IEF": "1", "Fonte": "100", "FonteRJ": "100",
        "TipoDetalhamentoFonte": "0", "DetalhamentoFonte": "0000",
        "DomicilioBancario":         "0001",

        "InscricaoGenerica":         "12345",           # código da IG
        "TipoInscricaoGenerica":     "01 - Bloqueio",   # tipo da IG (opcional)
    },

    # ── NA com Estorno ─────────────────────────────────────────────────────
    "NA_ESTORNO": {
        "UG":                        "123456",
        "Estorno":                   True,   # marca a caixa de estorno

        "TipoPatrimonial":           "Ativo",
        "ItemPatrimonial":           "Aplicações Financeiras",
        "OperacaoPatrimonial":       "Resgate",
        "IEF": "1", "Fonte": "100", "FonteRJ": "100",
        "TipoDetalhamentoFonte": "0", "DetalhamentoFonte": "0000",
        "DomicilioBancario":         "0001 - BANCO DO BRASIL",
    },
}

Dicas importantes

  • Os textos dos dropdowns devem ser exatamente iguais aos exibidos no SIAFE (copie direto da tela).
  • Use "SelecaoPorValor": True quando o dropdown tem muitas opções e é mais confiável selecionar pelo código numérico.
  • O DataFrame deve ter pelo menos as colunas: id, data, valor, observacao, tipo_id, num_documento (inicialmente None), tempo_contab (inicialmente None).

Instalação

O módulo apipoint (SharePoint e Graph API) depende de uma versão específica do office365-rest-python-client que ainda não está publicada no PyPI. Instale-a manualmente antes do jupiter:

pip install git+https://github.com/vgrem/office365-rest-python-client.git@630e4e1f7977e28a8354174cc85896421425e4d7
pip install jupiter-subtes

Se você não usar o apipoint, a instalação simples é suficiente:

pip install jupiter-subtes

Dependências

Biblioteca Instalação Uso
selenium >= 4.0.0 automática Controle do navegador
automaweb automática Biblioteca base de automação web (interna)
pandas automática Manipulação de DataFrames
requests automática Chamadas à API REST do SIAFE
msal automática Autenticação Microsoft (Graph API)
office365-rest-python-client manual (ver acima) SharePoint e Graph API

O Google Chrome deve estar instalado e atualizado. O automaweb gerencia o ChromeDriver automaticamente.


Logging

A biblioteca não configura nenhum handler de log por conta própria. Para capturar os erros em arquivo:

import logging

logger = logging.getLogger("jupiter")
handler = logging.FileHandler("automacao.log", encoding="utf-8")
handler.setFormatter(logging.Formatter("%(asctime)s | %(levelname)s | %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.ERROR)

Arquitetura e Decisões de Design

  • Separação de regras de negócio: Siafe não contém nenhum código de conta, fonte ou UG. Tudo é injetado via dict_map, permitindo que a mesma biblioteca atenda a diferentes sub-setores sem alterações no código-fonte.
  • Validação antes de prosseguir: após cada preenchimento crítico, o robô relê o campo do SIAFE para confirmar que o valor foi aceito pelo sistema. Se a validação falhar, ele clica em "Voltar" e tenta novamente (até 3 tentativas por lançamento).
  • Resiliência a falhas de DOM: todos os métodos de interação com o navegador têm retry automático para StaleElementReferenceException e aguardam o cursor sair do estado wait antes de prosseguir.
  • XPaths centralizados: os seletores ficam nos arquivos _xpaths.py e não no código de automação, facilitando a manutenção quando o SIAFE ou o SEI atualizam a interface.
  • Callback de sucesso: gerar_documento aceita um callback_sucesso chamado a cada documento gerado, permitindo persistência incremental — se o processo cair na metade, os documentos já contabilizados estão salvos.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jupiter_subtes-1.1.1.tar.gz (45.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jupiter_subtes-1.1.1-py3-none-any.whl (40.6 kB view details)

Uploaded Python 3

File details

Details for the file jupiter_subtes-1.1.1.tar.gz.

File metadata

  • Download URL: jupiter_subtes-1.1.1.tar.gz
  • Upload date:
  • Size: 45.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for jupiter_subtes-1.1.1.tar.gz
Algorithm Hash digest
SHA256 deb426d1719dc3b80ee79f2f28101bd7e87c8947b524a42cc0d620e3c32a6466
MD5 3770820a17a7e700facd5bf4777eb78b
BLAKE2b-256 8c57204c4fc3f7a6297792da9d5211f1e44c543016010ac394168ceb6428d98d

See more details on using hashes here.

File details

Details for the file jupiter_subtes-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: jupiter_subtes-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 40.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for jupiter_subtes-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 017e68590e941d888e7e040742cfa2a295c688b0d628dc443f807ee7a25205e7
MD5 45306332ed5e359d7cd42b86f2996998
BLAKE2b-256 cb20f2e3c6aedec406fd19be7e7e402dbe97ce3bf5e2e37ecebced897d71a4ef

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page