Files
GTI_GERENCIADOR_FISCAL/gerenciador/README.md
T

6.5 KiB

painel-clientes-web

Painel administrativo (FastAPI + Jinja2) para gestão de CLIENTE, CLIENTE_CONTATO e geração manual de chave de licenciamento no MySQL do sistema legado.

Proteções: login por sessão em toda rota (cookie assinado — ver seção "Login e permissões" abaixo), CSRF via cookie de duplo envio (app/csrf.py — todo formulário que muda estado embute o mesmo token do cookie csrf_token, definido pelo middleware em app/main.py) e a lista de clientes é paginada (100 por página), com busca e ordenação por código/razão/fantasia/CNPJ.

CODIGO de cliente não aceita duplicado (bloqueio duro, sem bypass) e o formulário de novo cliente já sugere o próximo código sequencial (editável). Só CODIGO, CNPJ, RAZAO e FANTASIA são obrigatórios — os campos de contrato (implantação, mensalidade, juros, datas) podem ficar em branco e recebem default (0,00 / data de hoje).

CLIENTE tem uma coluna ATIVO (criada no banco de dev — precisa ser criada manualmente em produção também, ALTER TABLE completo no início de app/db/clientes.py) que controla o filtro de status da listagem (Ativos/Inativos/Todos, padrão Ativos). Sem essa coluna, a listagem quebra com erro 500. O DEFAULT 1 do ALTER TABLE é intencional: todo cliente cadastrado antes da migração nasce como Ativo.

A rota /log mostra o histórico de ações do painel (tabela LOG_PAINEL, criada no banco de dev — precisa ser criada manualmente em produção também, DDL no início de app/db/log.py) e, se N8N_API_URL/N8N_API_KEY estiverem configuradas (.env.example), os últimos envios automáticos de chave via WhatsApp (lidos da Data Table pagamentos_processados do n8n, somente leitura).

Login e permissões

O painel usa login por sessão (cookie assinado, app/session.py) — não é mais HTTP Basic Auth. Defina SESSION_SECRET no .env (.env.example tem o exemplo); sem isso, uma chave é gerada em memória a cada start do processo, o que derruba todas as sessões ativas a cada restart (funciona, só incomoda).

Cada funcionário (FUNCIONARIO_PAINEL, criada no banco de dev — precisa ser criada manualmente em produção também, DDL no início de app/db/funcionarios.py) tem um perfil: admin (gerencia outros funcionários em /funcionarios: cadastra, edita, redefine senha, ativa/desativa, promove/rebaixa perfil) ou padrão (sem acesso a /funcionarios). Se FUNCIONARIO_PAINEL já existir de uma instalação anterior sem a coluna PERFIL, rode o ALTER TABLE FUNCIONARIO_PAINEL ADD COLUMN PERFIL ... (DDL completo em app/db/funcionarios.py) — precisa ser rodado manualmente em produção também; sem essa coluna, todo login falha com erro 500 (a query de autenticação seleciona PERFIL, que não existe na tabela antiga). O DEFAULT 'ADMIN' desse ALTER TABLE é intencional: todo funcionário cadastrado antes da migração vira admin automaticamente. O sistema nunca deixa ficar sem nenhum admin ativo — rebaixar ou desativar o último admin é bloqueado.

Qualquer usuário logado troca a própria senha em Minha conta (/minha-conta), pedindo a senha atual antes de trocar.

ADMIN_USER/ADMIN_PASSWORD (.env.example) continuam funcionando como conta de bootstrap, sempre com perfil admin — é com ela que você entra a primeira vez para cadastrar o primeiro funcionário, e serve de plano B se a tabela ficar vazia. Login checa primeiro o admin do .env, depois FUNCIONARIO_PAINEL (só contas com ATIVO=1).

Rodando localmente (sem Docker)

  1. Instale as dependências de desenvolvimento:
    pip install -r requirements-dev.txt
    
  2. Defina as variáveis de ambiente necessárias (ADMIN_USER, ADMIN_PASSWORD, DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD, SESSION_SECRET — veja .env.example) diretamente no shell antes de subir a aplicação. SESSION_SECRET é opcional em dev — sem ela, uma chave aleatória é gerada em memória a cada start (sessões caem a cada restart); defina um valor fixo em produção. Importante: apesar de python-dotenv estar nas dependências, nada no código chama load_dotenv() hoje — um arquivo .env sozinho NÃO é lido automaticamente fora do Docker. Exporte as variáveis manualmente (ex.: set -a; source .env; set +a no bash, ou $env:... no PowerShell) ou defina-as por outro meio.
  3. Rode a aplicação:
    uvicorn app.main:app --reload
    
  4. Rodando direto na máquina (fora de container), use DB_HOST=127.0.0.1 (porta 3307, a mesma do container de dev licenciamento-mysql-dev).

Rodando os testes

pytest -v

Os testes usam defaults definidos em tests/conftest.py (equivalentes ao .env.example) e incluem testes de integração que precisam de um MySQL em 127.0.0.1:3307.

Ambiente de teste local (MySQL descartável)

Fora do repositório original (BLOQUEIO SGV), este subtree não tem acesso ao container licenciamento-mysql-dev nem ao dump real de clientes — nem deveria, dado que este monorepo não importa dado real de nenhum cliente. Para rodar a suíte de forma autocontida, este diretório traz um MySQL de teste descartável, separado do docker-compose.yml de produção:

docker compose -f docker-compose.test.yml up -d

Isso sobe um mysql:5.7 só com a estrutura das tabelas (CLIENTE, CLIENTE_CONTATO, Clientes_Chaves, FUNCIONARIO_PAINEL, LOG_PAINEL) a partir de tests/db/schema.sql — DDL apenas, nenhum dado de cliente. Cada tabela do schema.sql tem, em comentário, a fonte exata de onde a definição foi extraída (docstring do módulo Python correspondente, ou a documentação de schema do sistema legado). O banco roda em tmpfs (nunca grava em disco, nunca sobrevive a um restart do container) e só escuta em 127.0.0.1:3307 — sem rede externa, sem volume nomeado, sem credencial real (senha devpassword123, válida só para este banco descartável).

Depois de rodar os testes:

docker compose -f docker-compose.test.yml down -v

remove o container e a rede — nada persiste.

Rodando via Docker

  1. Copie o arquivo de exemplo de variáveis de ambiente e edite ADMIN_PASSWORD (e demais valores, se necessário):
    cp .env.example .env
    
  2. Suba o container:
    docker compose up -d --build
    
    O docker-compose.yml já usa env_file: .env e mapeia host.docker.internal para o host (via extra_hosts), então o valor padrão DB_HOST=host.docker.internal do .env.example funciona também em hosts Docker Linux, não só no Docker Desktop (Windows/Mac).
  3. A aplicação fica disponível em http://localhost:8010.