Criação da bateria de testes automatizados, correção da pagina de gerar romaneio e plano da criação do modulo cliente
This commit is contained in:
195
README.md
195
README.md
@@ -3513,3 +3513,198 @@ Dockerfile # tira a linha de precompile mort
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>🆕 Atualização 28/08/2026 — PLANO: Módulo do Cliente (aprovado, NÃO implementado)</strong></summary>
|
||||
|
||||
> ⚠️ **Isto é um plano de implementação, não uma feature entregue.** Foi escrito
|
||||
> como ordem de serviço para ser executado em fases (inclusive por modelos
|
||||
> menores), com o levantamento do código já feito. Implementar **uma fase por
|
||||
> vez**, na ordem, validando cada uma antes da próxima.
|
||||
|
||||
## O que é o módulo
|
||||
|
||||
Hoje o cliente (Gade Hospitalar) manda uma **planilha** por mês/operação, que
|
||||
alguém sobe no banco como tabela `gade_entregas_*` (fora do sistema). O módulo
|
||||
do cliente substitui esse fluxo: uma área **`/cliente`** com login próprio onde
|
||||
o próprio cliente:
|
||||
|
||||
1. **Cria operações** (ex.: "UBS NORTE — setembro/2026") e lança entregas nelas;
|
||||
2. **Adiciona entregas em operações existentes**;
|
||||
3. Lança **entregas avulsas** (fora de qualquer operação);
|
||||
4. Cadastra **em massa via planilha .xlsx** (o módulo é um espelho da planilha
|
||||
atual — mesmos campos), com conferência antes de gravar;
|
||||
5. Recebe **detecção de duplicidade na hora**: "você tem X apontamentos
|
||||
duplicados" e "a NF Y já está roteirizada / em curso".
|
||||
|
||||
## 🔒 Regras invioláveis (valem para TODAS as fases)
|
||||
|
||||
- `db_reem_simplerout_2026` e as `gade_entregas_*` são **somente leitura**:
|
||||
nunca migration, nunca INSERT/UPDATE/DELETE/DROP nelas. O módulo grava apenas
|
||||
em **tabelas próprias novas**.
|
||||
- Todo nome de tabela externa que entra em SQL passa por `Operacao.sanitizar` +
|
||||
`quote_table_name` (padrão já usado no projeto inteiro).
|
||||
- **NÃO transformar `app/models/operacao.rb` em ActiveRecord.** Ele é um PORO
|
||||
(só métodos de classe) que descobre as tabelas externas pelo catálogo do
|
||||
Postgres e é usado por ~8 services. Os models novos têm outros nomes.
|
||||
- Seguir o CLAUDE.md do projeto: número na tela é contrato; modo de visualização
|
||||
em aba, não tela nova; mobile-first com alvos de 48px; comentário explica o
|
||||
porquê; validar com Docker (`ruby -c` / actionview) antes de entregar.
|
||||
- Ao final de cada fase: listar os `git add` por nome (commit/push são do
|
||||
usuário) e lembrar que o deploy para o teste é manual.
|
||||
|
||||
## 📌 Fatos do código que o implementador precisa saber
|
||||
|
||||
| Assunto | Onde está | O que copiar |
|
||||
|---|---|---|
|
||||
| "Operação" hoje | `app/models/operacao.rb` (PORO) | catálogo/whitelist (`nomes_validos`, `sanitizar`, `label`, `MESES`) — só consumir |
|
||||
| Colunas da planilha do cliente | `app/services/simpli_route/planilha_carga.rb` (`COLUNAS_OP`) | `nota_fiscal`, `nome_completo`, `endereco_sem_complemento`, `complemento`, `supervisao`, `telefones`; geo nas gade é `lat`/`long` |
|
||||
| Esquema variável por mês | `app/services/romaneios/enriquecimento_gade.rb` | detecção de colunas via `conn.columns(tabela)`; só `nota_fiscal` é garantida |
|
||||
| Dedup existente (melhor modelo) | `app/services/romaneios/importador.rb#deduplicar` + `RomaneioLinha.chave_para` + `app/services/romaneios/normalizador.rb` | chave normalizada (NF + título + endereço), primeira ocorrência vence, contagem reportada |
|
||||
| Rastreio (roteirizado/em curso) | `app/models/entrega.rb` (`db_reem_simplerout_2026`, `readonly?=true`) | scopes `da_conta_gade`, `por_nf`; status da visita indica o andamento |
|
||||
| Ler .xlsx | gem `roo` (~2.10, já no Gemfile; usada no romaneio) | |
|
||||
| Gerar .xlsx | gem `caxlsx` (já no Gemfile; usada na planilha SimpliRoute) | |
|
||||
| Papéis/permissões | `app/models/user.rb` (enum `role`), `app/models/permissao.rb`, `app/models/perfil_acesso.rb`, policies em `app/policies/` | `Permissao::TODAS` + `GRUPOS` + `PADRAO_POR_ROLE` (esquecer o PADRAO tira acesso silenciosamente — aviso no próprio model) |
|
||||
| Migration de permissão (modelo) | `db/migrate/20260827000004_add_permissao_romaneio_aos_perfis.rb` | sincronizar perfis existentes |
|
||||
| Rotas | `config/routes.rb` | seguir o padrão `namespace :admin`; atenção ao aviso do Inflector pt-BR no próprio arquivo (singular == plural gera `_index`) |
|
||||
| Notificações (evento novo) | infra em `app/models/notificacao_*` / `app/services/notificacao/` | criar evento "cliente lançou entregas" na Fase 5 |
|
||||
|
||||
**Não existe hoje nenhum conceito de "cliente" no código** (nem model, nem
|
||||
coluna, nem namespace) — a fundação é a Fase 0.
|
||||
|
||||
## Fase 0 — Fundação de acesso (`/cliente` existe e loga)
|
||||
|
||||
1. `User`: adicionar `cliente: 5` ao enum `role`.
|
||||
2. `Permissao`: novas chaves no grupo novo `cliente`:
|
||||
`cliente.ver`, `cliente.operacoes_criar`, `cliente.entregas_lancar`,
|
||||
`cliente.importar_planilha`, `cliente.avulsas` — em `TODAS`, `GRUPOS` e
|
||||
`PADRAO_POR_ROLE` (role `cliente` recebe todas as `cliente.*` e NADA de
|
||||
dashboard/consolidação/admin).
|
||||
3. `HOME_POR_PERMISSAO` / `home_rota`: usuário com `cliente.ver` cai em `/cliente`.
|
||||
4. Rotas: `namespace :cliente` com `root`, `resources :operacoes` (conferir o
|
||||
plural com o Inflector), `resources :entregas`, `resource :importacao`.
|
||||
5. Layout `app/views/layouts/cliente.html.erb`: identidade visual do projeto
|
||||
(dark, laranja `#f97316`), **sem** menus de admin; menu próprio: Operações ·
|
||||
Avulsas · Importar planilha · Sair.
|
||||
6. Policies Pundit novas baseadas em `user.pode?('cliente.…')`; os controllers
|
||||
de `/cliente` herdam de um `Cliente::BaseController` que bloqueia quem não
|
||||
tem `cliente.ver`.
|
||||
7. Migration sincronizando perfis + seeds: perfil "Cliente" e um usuário de
|
||||
teste `cliente@reem.com`.
|
||||
|
||||
**Pronto quando**: login com usuário cliente cai em `/cliente` (vazio, com estado
|
||||
vazio instrutivo), e esse usuário NÃO acessa `/dashboard`, `/consolidacoes` nem
|
||||
`/admin/*` (redireciona para `/sem-acesso`); admin continua acessando tudo.
|
||||
|
||||
## Fase 1 — Modelagem (tabelas próprias)
|
||||
|
||||
1. `ClienteOperacao` (`cliente_operacoes`): `nome`, `mes`, `ano`,
|
||||
`status` (enum: `rascunho` → `enviada` → `roteirizada` → `fechada`),
|
||||
`criado_por_id` (FK users), timestamps. Índice único em
|
||||
`[nome, mes, ano]` (case-insensitive).
|
||||
2. `ClienteEntrega` (`cliente_entregas`): `cliente_operacao_id` (**nulo =
|
||||
avulsa**), campos espelhando a planilha atual — `nota_fiscal`,
|
||||
`nome_completo`, `endereco_sem_complemento`, `complemento`, `supervisao`,
|
||||
`telefones`, `lat`, `long`, `observacoes` — mais `chave_dedup` (string
|
||||
normalizada), `origem` (enum: `manual`/`planilha`), `criado_por_id`,
|
||||
timestamps.
|
||||
3. `chave_dedup`: gerada em callback com a MESMA normalização de
|
||||
`Romaneios::Normalizador` (extrair para um módulo compartilhável se preciso,
|
||||
sem quebrar os chamadores atuais). Índice único parcial em
|
||||
`[cliente_operacao_id, chave_dedup]` — e validação amigável antes do índice.
|
||||
4. Validações: `nota_fiscal` obrigatória; `nome_completo` ou endereço presentes.
|
||||
|
||||
**Pronto quando**: migrations rodam no Docker, models com validações e specs de
|
||||
unicidade; nada de tela ainda.
|
||||
|
||||
## Fase 2 — Telas `/cliente` (cadastro individual intuitivo)
|
||||
|
||||
1. **Lista de operações**: cards (nome, mês/ano, nº de entregas, status, botão
|
||||
"Adicionar entrega"); estado vazio dizendo o que fazer ("Crie sua primeira
|
||||
operação ou importe uma planilha").
|
||||
2. **Criar operação**: form curto (nome + mês/ano), sem jargão técnico.
|
||||
3. **Adicionar entrega**: form mobile-first (alvos 48–56px), campos na ordem da
|
||||
planilha, busca de endereço livre; ao salvar, o painel de duplicidade da
|
||||
Fase 4 responde na mesma tela (inline, não outra página).
|
||||
4. **Entregas avulsas**: mesma tela de lançamento com aba/rótulo "Avulsa
|
||||
(sem operação)" — mesma unidade, rótulo explícito (regra 1 do CLAUDE.md).
|
||||
5. Lista de entregas da operação com edição inline (padrão Stimulus do projeto,
|
||||
ver `romaneio_controller.js`) e remoção com confirmação.
|
||||
|
||||
**Pronto quando**: fluxo completo criar operação → lançar 3 entregas → editar →
|
||||
remover, no desktop e no celular (validar com agent-browser + `set device`).
|
||||
|
||||
## Fase 3 — Importação em massa (.xlsx)
|
||||
|
||||
1. **Modelo para download**: gerar com `caxlsx` uma planilha com o cabeçalho
|
||||
exato dos campos da Fase 1 + 2 linhas de exemplo (o cliente já conhece esse
|
||||
formato — é a planilha que ele manda hoje).
|
||||
2. **Upload**: `roo` lendo .xlsx; mapear cabeçalhos com tolerância (acentos,
|
||||
caixa, espaços — reusar normalização existente).
|
||||
3. **Tela de conferência ANTES de gravar** (nada entra direto): resumo com
|
||||
N válidas · N com erro (motivo por linha) · N duplicadas (painel da Fase 4);
|
||||
o cliente escolhe "Importar só as válidas" ou cancelar.
|
||||
4. Gravação transacional (`insert_all` ou transaction com validação); relatório
|
||||
final na tela com os mesmos números da conferência.
|
||||
5. Limites e mensagens: arquivo até ~5 MB / ~5.000 linhas; erro de formato tem
|
||||
mensagem em português dizendo o que corrigir.
|
||||
|
||||
**Pronto quando**: importar a planilha-modelo preenchida com linhas repetidas de
|
||||
propósito grava só as válidas e reporta as duplicadas com os números certos.
|
||||
|
||||
## Fase 4 — Duplicidade inteligente (`Cliente::DetectorDuplicidade`)
|
||||
|
||||
Serviço único usado pelo form individual (Fase 2) e pela importação (Fase 3).
|
||||
Entrada: coleção de entregas candidatas (+ operação alvo). Saída: para cada
|
||||
candidata, `status_duplicidade` e a evidência. **Quatro camadas, nesta ordem:**
|
||||
|
||||
1. **Dentro do lote**: chave normalizada repetida no próprio arquivo/form —
|
||||
padrão de `Romaneios::Importador#deduplicar` (primeira vence, conta o resto).
|
||||
2. **Contra o módulo**: `ClienteEntrega` já gravada com a mesma `chave_dedup`
|
||||
(na mesma operação ou avulsa do mesmo mês).
|
||||
3. **Contra as `gade_entregas_*` do mesmo mês/ano**: SELECT read-only (tabelas
|
||||
via `Operacao.sanitizar` + `quote_table_name`; só `nota_fiscal` é garantida —
|
||||
detectar colunas via `conn.columns` como no `EnriquecimentoGade`).
|
||||
4. **Contra o rastreio** (`Entrega.da_conta_gade.por_nf`): se a NF já existe no
|
||||
espelho, classificar pelo status da visita — **"já roteirizada"** (planejada)
|
||||
ou **"em curso/entregue"** (checkout feito). Conferir os valores reais de
|
||||
status no espelho antes de fixar o mapeamento.
|
||||
|
||||
Na tela (regra 1 do CLAUDE.md — explicar onde o número aparece):
|
||||
resumo "⚠️ X apontamentos duplicados neste lançamento" + lista expandível
|
||||
inline com a camada que acusou cada um ("já lançada nesta operação",
|
||||
"consta na planilha de agosto", "NF 79093 já roteirizada em 27/08").
|
||||
Duplicata **avisa e bloqueia por padrão**, com opção explícita "lançar mesmo
|
||||
assim" (grava com flag `duplicada_confirmada` para auditoria).
|
||||
|
||||
**Pronto quando**: os 4 cenários têm teste (fixtures/factories) e a tela mostra
|
||||
a contagem e o motivo camada por camada.
|
||||
|
||||
## Fase 5 — Lado admin + integração
|
||||
|
||||
1. **Visão admin**: aba/tela em `/admin` listando lançamentos do cliente por
|
||||
operação/status, com os MESMOS números que o cliente vê (recorte único).
|
||||
2. **Exportação**: baixar as entregas de uma `ClienteOperacao` no formato da
|
||||
planilha de carga do SimpliRoute (referência: `SimpliRoute::PlanilhaCarga`) —
|
||||
é o que fecha o ciclo: o que o cliente lança vira rota.
|
||||
3. **Notificação**: evento novo na infra existente ("Cliente lançou N entregas
|
||||
na operação X") para o grupo do WhatsApp da operação.
|
||||
4. **Auditoria**: registrar criar/editar/excluir/importar do módulo no padrão de
|
||||
auditoria existente (`/admin/auditoria_logs`).
|
||||
5. Transição de status da operação (`enviada` → `roteirizada`) — manual pelo
|
||||
admin nesta fase; automatizar via rastreio fica para depois.
|
||||
|
||||
**Pronto quando**: admin vê, exporta e é notificado; auditoria registra.
|
||||
|
||||
## Fase 6 — Validação e encerramento
|
||||
|
||||
1. Adicionar à `docs/BATERIA-DE-TESTES.md` os casos: login cliente; criar
|
||||
operação; lançar entrega; importar .xlsx com duplicatas propositais e conferir
|
||||
as contagens; bloqueio de acesso do cliente às áreas de admin.
|
||||
2. Rodar a bateria completa (`/bateria-testes`) no ambiente de teste após o
|
||||
deploy manual.
|
||||
3. Atualizar este README (marcar fases entregues) e o perfil "Cliente" nos seeds.
|
||||
|
||||
</details>
|
||||
|
||||
Reference in New Issue
Block a user