chore: remove the abandoned prototypes and archive what described them
portal/, kanban/ and agente/ were 2,034 lines implementing the original Tiny-first model: token upload links, a second SQLite Kanban, a factory agent. Nothing imported or started any of it, and several endpoints took the acting user from the request body with no authentication at all. Their real cost was that a reader arriving at this repository found two Kanbans and two portals and had to work out which one was real. The root schema.sql and .env.exemplo went with them: both code paths load local/schema.sql, and having .env.exemplo beside .env.example differing by one letter was a trap rather than a convenience. The documents describing that model are archived rather than deleted. They record decisions and reasoning the current documents do not repeat, so they are worth keeping as background, with a header saying plainly that they are not instructions. README.md keeps its business case — the capacity figures and the cost argument are still the reason this project exists — but now states where the prototype documentation begins and that the code it describes is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
328
docs/historico/API.md
Normal file
328
docs/historico/API.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# API — contratos
|
||||
|
||||
> Historical prototype contracts only. The active local API is documented at
|
||||
> [localhost:8000/docs](http://localhost:8000/docs) and in [LOCAL_SETUP.md](LOCAL_SETUP.md).
|
||||
> `CONTEXT.md` overrides the legacy endpoints and workflows below.
|
||||
|
||||
> Todos os endpoints do módulo DTF, com requisição e resposta.
|
||||
> Base: `portal/main.py` e `kanban/main.py`.
|
||||
|
||||
---
|
||||
|
||||
## Autenticação
|
||||
|
||||
| Quem chama | Como se autentica |
|
||||
|---|---|
|
||||
| Tiny (webhook) | header `x-token` com `WEBHOOK_TOKEN` |
|
||||
| Cliente | token no path da URL — sem login |
|
||||
| Agente | header `x-token` com `AGENTE_TOKEN` |
|
||||
| Kanban (aba do PCP) | sessão do PCP — **rede interna, sem porta para fora** |
|
||||
|
||||
---
|
||||
|
||||
# PORTAL · na nuvem
|
||||
|
||||
## `POST /webhook/tiny`
|
||||
|
||||
Chamado quando nasce um pedido de DTF. **É o gatilho de tudo.**
|
||||
|
||||
```json
|
||||
{ "numero": "48213", "cliente": "Estamparia Vitória",
|
||||
"telefone": "5516999998888", "metros": 2.4, "cliente_novo": false }
|
||||
```
|
||||
|
||||
**200**
|
||||
```json
|
||||
{ "ok": true, "link": "https://arte.dropstaratacado.com.br/arte/a7f3k9..." }
|
||||
```
|
||||
|
||||
Cria o pedido, gera token de 7 dias e enfileira o WhatsApp com o link.
|
||||
Se o número já existir, devolve `{"ok":true,"ja_existia":true}` sem duplicar.
|
||||
|
||||
**Latência importa.** O link só sai depois que o pedido existe no Tiny — se a
|
||||
integração VNDA → Tiny demorar, o cliente espera. Medir antes de subir.
|
||||
|
||||
---
|
||||
|
||||
## `GET /arte/{token}`
|
||||
|
||||
Página de upload. HTML. O token já sabe o pedido — **o cliente não digita nada.**
|
||||
|
||||
## `GET /api/arte/{token}`
|
||||
|
||||
```json
|
||||
{ "pedido": "48213", "cliente": "Estamparia Vitória", "metros": 2.4,
|
||||
"area_util_cm": 97, "largura_max_cm": 57,
|
||||
"limite_arquivo_m": 20, "max_gb": 5, "max_arquivos": 10 }
|
||||
```
|
||||
|
||||
**410** se o token expirou.
|
||||
|
||||
## `POST /api/arte/{token}/url`
|
||||
|
||||
```json
|
||||
{ "nome": "estampa.png", "bytes": 84000000 }
|
||||
```
|
||||
```json
|
||||
{ "url": "https://r2.../48213/ab12_estampa.png?X-Amz-...",
|
||||
"chave": "48213/ab12_estampa.png", "expira_em": 3600 }
|
||||
```
|
||||
|
||||
**Upload direto para o storage.** Arquivo de 5 GB passando pelo VPS derrubaria
|
||||
o processo. Em partes, para arquivos grandes.
|
||||
|
||||
## `POST /api/arte/{token}/pronto`
|
||||
|
||||
```json
|
||||
{ "chave": "48213/ab12_estampa.png", "repeticoes": 20 }
|
||||
```
|
||||
```json
|
||||
{ "arte_id": 991, "status": "processando" }
|
||||
```
|
||||
|
||||
Dispara o pré-flight em background. O cliente acompanha por polling em
|
||||
`GET /api/arte/{token}/status`.
|
||||
|
||||
## `GET /api/arte/{token}/status`
|
||||
|
||||
**Aprovada:**
|
||||
```json
|
||||
{ "status": "aprovada", "dpi_efetivo": 300, "qualidade_pct": 100,
|
||||
"metros_totais": 48.0, "minutos_maquina": 144,
|
||||
"partes": [{"ordem":1,"metros":20,"nome":"48213_p1.png"},
|
||||
{"ordem":2,"metros":20,"nome":"48213_p2.png"},
|
||||
{"ordem":3,"metros":8,"nome":"48213_p3_carimbado.png"}],
|
||||
"avisos": [], "previsao_saida": "2026-09-02T18:00:00-03:00" }
|
||||
```
|
||||
|
||||
**Recusada:**
|
||||
```json
|
||||
{ "status": "recusada",
|
||||
"motivo": "A resolução real da arte é de 72 DPI no tamanho que você comprou. Precisamos de pelo menos 150 DPI — o ideal é 300. Reenvie em maior resolução." }
|
||||
```
|
||||
|
||||
**O relógio do prazo só começa quando `status = aprovada`.** Arquivo ruim
|
||||
enviado às 17h não faz as horas correrem contra a casa.
|
||||
|
||||
---
|
||||
|
||||
## `GET /api/artes` · o agente busca
|
||||
|
||||
Header `x-token`. Devolve o que está aprovado, com vírus liberado e ainda não baixado.
|
||||
|
||||
```json
|
||||
[{ "arte_id": 991, "pedido": "48213", "cliente": "Estamparia Vitória",
|
||||
"metros": 48.0, "minutos_maquina": 144, "conferir_manual": false,
|
||||
"partes": [{"ordem":1,"metros":20,"nome":"48213_p1.png",
|
||||
"url":"https://r2.../...","sha256":"a3f9..."}] }]
|
||||
```
|
||||
|
||||
## `POST /api/artes/{id}/baixada` · confirma o download
|
||||
## `POST /api/agente/heartbeat` · a cada 5 min
|
||||
|
||||
**Sem sinal por 15 minutos, alertar o TI.** Serviço silencioso parado é pior que
|
||||
erro barulhento: ninguém percebe até o cliente cobrar.
|
||||
|
||||
---
|
||||
|
||||
## `GET /cliente/{token_cliente}` · minhas artes
|
||||
|
||||
Link permanente do cliente, não do pedido. Lista as artes tratadas dos últimos
|
||||
12 meses.
|
||||
|
||||
## `POST /cliente/{token_cliente}/reimprimir`
|
||||
|
||||
```json
|
||||
{ "arte_id": 812, "metros": 20 }
|
||||
```
|
||||
|
||||
Abre pedido novo no Tiny com a arte já pronta. **Recompra sem atrito.**
|
||||
|
||||
---
|
||||
|
||||
# KANBAN · rede interna, aba do PCP
|
||||
|
||||
## `GET /api/quadro`
|
||||
|
||||
```json
|
||||
{ "colunas": [{"id":"rec","nome":"Arte recebida"}, ...],
|
||||
"cards": {
|
||||
"fil": [{ "arte_id": 991, "pedido": "48213", "cliente": "Estamparia Vitória",
|
||||
"metros": 48.0, "minutos_maquina": 144, "partes": 3,
|
||||
"maquina": null, "desde": "2026-09-02T14:02:00",
|
||||
"parado_seg": 4320, "conferir_manual": false }]
|
||||
},
|
||||
"maquinas": [{ "n": 1, "ocupada": true, "pedido": "48190",
|
||||
"metros": 9.0, "rodando_seg": 720 },
|
||||
{ "n": 3, "ocupada": false }] }
|
||||
```
|
||||
|
||||
`maquinas` é o que pinta o **círculo vermelho**. Com seis máquinas vendo o mesmo
|
||||
quadro, é o que evita dois operadores no mesmo arquivo.
|
||||
|
||||
---
|
||||
|
||||
## `POST /api/puxar`
|
||||
|
||||
```json
|
||||
{ "maquina": 4, "usuario": "alexandre" }
|
||||
```
|
||||
```json
|
||||
{ "maquina": "Maq 4", "pedido": "48197", "metros": 18.0,
|
||||
"minutos": 60, "reserva_expira_em": 3 }
|
||||
```
|
||||
|
||||
**Um pedido por vez** e **reserva de 3 minutos** — ajustes pedidos pela sala.
|
||||
Pega sempre o mais antigo da fila. Se não entrar em `imp` em 3 min, volta.
|
||||
|
||||
**409** se a máquina já estiver ocupada. **404** se a fila estiver vazia.
|
||||
|
||||
## `POST /api/devolver`
|
||||
|
||||
```json
|
||||
{ "arte_id": 991, "usuario": "thales", "motivo": "travou no meio" }
|
||||
```
|
||||
|
||||
Volta ao **topo** da fila, não ao fim — o pedido já esperou uma vez.
|
||||
|
||||
## `POST /api/mover`
|
||||
|
||||
```json
|
||||
{ "arte_id": 991, "para": "fin", "usuario": "poliana", "maquina": 4 }
|
||||
```
|
||||
|
||||
Grava o movimento **antes** de qualquer integração externa. Se o Tiny estiver
|
||||
fora, o job fica na fila e tenta de novo em 1, 5 e 30 min — **mas a medição de
|
||||
tempo já está salva.**
|
||||
|
||||
Move o arquivo entre as pastas e enfileira marcador e WhatsApp quando a coluna
|
||||
pedir. Ao ir para `apc`, pergunta o motivo da prova de cor.
|
||||
|
||||
## `PATCH /api/card/{arte_id}`
|
||||
|
||||
```json
|
||||
{ "minutos_maquina": 90 }
|
||||
```
|
||||
|
||||
Ajuste da estimativa. **O sistema sugere por `metros/20*60`; quem trata a arte
|
||||
corrige só quando foge do normal.** É esse número que soma a fila contra a
|
||||
capacidade do dia.
|
||||
|
||||
## `GET /p/{pedido}`
|
||||
|
||||
O QR do carimbo aponta para cá. Redireciona para o card. **A revisão bipa e cai
|
||||
na tela do pedido** em vez de procurar na lista.
|
||||
|
||||
---
|
||||
|
||||
## Retrabalho
|
||||
|
||||
### `POST /api/retrabalho`
|
||||
|
||||
```json
|
||||
{ "pedido_origem": "48184", "metros": 2.0, "causa": "impressao",
|
||||
"aberto_por": "mayana", "evidencia": "print-whatsapp.jpg" }
|
||||
```
|
||||
```json
|
||||
{ "id": 44, "alcada": "sala", "vez": 1, "conta_na_meta": true }
|
||||
```
|
||||
|
||||
**A Mayana abre e classifica** — conhece o cliente e a reclamação.
|
||||
**A causa fica travada.** Quem discorda contesta, não altera.
|
||||
|
||||
### `POST /api/retrabalho/{id}/autorizar`
|
||||
|
||||
```json
|
||||
{ "usuario": "thales" }
|
||||
```
|
||||
|
||||
**Thales ou Alexandre autorizam**, porque o retrabalho da casa entra na meta
|
||||
deles. Eles confirmaram achar justo.
|
||||
|
||||
**403** se a alçada exigir financeiro ou diretoria.
|
||||
|
||||
### `POST /api/retrabalho/{id}/contestar`
|
||||
|
||||
```json
|
||||
{ "usuario": "alexandre", "texto": "não foi impressão, a arte veio em CMYK" }
|
||||
```
|
||||
|
||||
Registra a discordância sem mudar a causa. Fica com quem pediu e quem decidiu.
|
||||
|
||||
---
|
||||
|
||||
## Relatórios
|
||||
|
||||
### `GET /api/relatorio/etapas?dias=7`
|
||||
|
||||
```json
|
||||
[{ "coluna": "tra", "nome": "Arte tratada", "movimentos": 84,
|
||||
"media_min": 94, "pior_min": 380 }]
|
||||
```
|
||||
|
||||
**Responde a pergunta que decide o terceiro turno:** quanto do tempo é fila e
|
||||
quanto é trabalho. Se a arte demora 6h e a máquina imprime em 40 min, rodar 24h
|
||||
só faz a fila esperar de madrugada.
|
||||
|
||||
### `GET /api/relatorio/impressao?dias=7`
|
||||
|
||||
```json
|
||||
[{ "maquina": "Maq 1", "pedidos": 42, "media_min": 34,
|
||||
"metros": 310.5, "m_por_hora": 20.4 }]
|
||||
```
|
||||
|
||||
**`m_por_hora` valida ou derruba os 20 m/h.** Todo o cálculo de capacidade —
|
||||
60.480 metros/mês, R$ 148 mil de ganho — depende desse número. Se for 14, a
|
||||
conta muda inteira.
|
||||
|
||||
### `GET /api/relatorio/aproveitamento?dias=30`
|
||||
|
||||
```json
|
||||
{ "metros_faturados": 11605, "metros_de_filme": 12693,
|
||||
"aproveitamento_pct": 91.4, "valor_do_ponto_mes": 980,
|
||||
"fonte": "transferências para o depósito Sala DTF no Tiny" }
|
||||
```
|
||||
|
||||
**Não exige apontamento novo.** O consumo já é registrado quando o insumo é
|
||||
transferido para o depósito de impressão — a Altus faz isso porque também
|
||||
revende insumo.
|
||||
|
||||
### `GET /api/relatorio/retrabalho?dias=30`
|
||||
|
||||
```json
|
||||
{ "por_causa": [{ "causa": "impressao", "descricao": "Falha de impressão",
|
||||
"responsavel": "Thales e Alexandre", "pedidos": 9,
|
||||
"metros": 24.5, "pct": 1.1, "meta": 0.4, "bate": false }],
|
||||
"total_casa_pct": 2.9, "meta_casa_pct": 1.6 }
|
||||
```
|
||||
|
||||
**⚠ A meta ainda não está decidida.** A proposta era 0,4% por causa; a sala
|
||||
respondeu que "meta geral seria melhor". `META_POR_CAUSA` está isolado no código
|
||||
para trocar sem mexer no resto.
|
||||
|
||||
---
|
||||
|
||||
## Códigos de erro
|
||||
|
||||
| Código | Quando |
|
||||
|---|---|
|
||||
| 400 | payload inválido, coluna inexistente, mais de 10 arquivos |
|
||||
| 401 | token do webhook ou do agente errado |
|
||||
| 403 | alçada insuficiente para autorizar retrabalho |
|
||||
| 404 | token não existe, card não encontrado, fila vazia |
|
||||
| 409 | máquina já ocupada, pedido já reservado |
|
||||
| 410 | token do link expirou |
|
||||
| 413 | arquivo acima de 5 GB |
|
||||
| 429 | rate limit — 20 req/min por token |
|
||||
|
||||
---
|
||||
|
||||
## O que testar antes de dar por pronto
|
||||
|
||||
- [ ] Upload de arquivo de 5 GB sem derrubar o VPS
|
||||
- [ ] Dois operadores clicando `puxar` ao mesmo tempo — só um pega
|
||||
- [ ] Reserva expirando: puxar e não mover em 3 min devolve à fila
|
||||
- [ ] Tiny fora do ar: o movimento grava e o job fica na fila
|
||||
- [ ] Agente sem internet: para, e ao voltar baixa o acumulado
|
||||
- [ ] Arquivo baixado pela metade não aparece no kanban
|
||||
- [ ] Token expirado devolve 410 com mensagem clara ao cliente
|
||||
307
docs/historico/ESPECIFICACAO.md
Normal file
307
docs/historico/ESPECIFICACAO.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Módulo DTF no PCP — especificação de implementação
|
||||
|
||||
> Para o Wagner. Consolida o desenho depois das reuniões com os designers e com
|
||||
> a sala de impressão em 02/09/2026.
|
||||
> Complementa o `README.md` (arquitetura) e o código em `portal/`, `agente/` e
|
||||
> `kanban/`.
|
||||
|
||||
---
|
||||
|
||||
## O que mudou depois das reuniões
|
||||
|
||||
Sete decisões vieram da equipe e **já estão refletidas no código**. Se algo no
|
||||
código parecer estranho, provavelmente é uma delas.
|
||||
|
||||
| Item | Era | Ficou | Quem definiu |
|
||||
|---|---|---|---|
|
||||
| Reserva ao puxar | 15 min | **3 min** | sala |
|
||||
| Puxar trabalho | até 30 min | **um pedido por vez** | sala |
|
||||
| Marcadores de duração | seriam apagados | **viram campo `minutos_maquina`** | sala |
|
||||
| Hot folder em rede | a confirmar | **aceita** | sala |
|
||||
| Production Manager | a confirmar | **centraliza, mas o PC central precisa de mais capacidade** | sala |
|
||||
| Licenças do Flexi | a confirmar | **por PC** | sala |
|
||||
| Meta de retrabalho | 0,4% por causa | **rever: a sala prefere meta geral** | sala |
|
||||
|
||||
---
|
||||
|
||||
## Números que a equipe deu e que entram no cálculo
|
||||
|
||||
| Dado | Valor | De onde veio |
|
||||
|---|---|---|
|
||||
| Arquivos que chegam usáveis | **30%** | designers |
|
||||
| Tempo para tratar arquivo bom | **3 minutos** | designers |
|
||||
| Tempo para tratar arquivo ruim | **1h30** | designers |
|
||||
| Formatos que mais chegam | **PNG e PDF** | designers |
|
||||
| PDF que chega | **vetorial** | designers |
|
||||
| SPOT | **sempre igual** | designers |
|
||||
| Cores que sempre dão problema | azul, vermelho, castanho, laranja, verde — **secundárias** | sala |
|
||||
| Prova de cor hoje | **não mandam para ninguém** | sala |
|
||||
| Como o operador sabe o próximo | **notinha do pedido** | sala |
|
||||
| Tempos de setup | **"não temos informação"** | sala |
|
||||
|
||||
**O de 30% é o mais consequente.** Sete de cada dez arquivos passam pelo
|
||||
designer. Se o pré-flight barrar metade dos ruins na entrada, são ~3,5 arquivos
|
||||
em 10 que deixam de consumir tempo de arte.
|
||||
|
||||
---
|
||||
|
||||
## Fases de implementação
|
||||
|
||||
Cada fase entrega valor sozinha. **Não espere a fase 4 para colocar algo no ar.**
|
||||
|
||||
### Fase 1 · Limpeza do Tiny — pode começar hoje
|
||||
|
||||
Não depende de nada nem de ninguém.
|
||||
|
||||
- [ ] Criar os marcadores `DTF-PRODUCAO`, `DTF-PRONTO` e `DTF-PROVA-COR`
|
||||
- [ ] Tirar da **lista de sugestão** as variações digitadas à mão: `inicio 14:45`,
|
||||
`inicio13:34`, `1hrs`, `3h`, `+30min de correcao` e as demais
|
||||
- [ ] **Não apagar do histórico dos pedidos** — só da lista de opções
|
||||
- [ ] **Preservar** `30 min`, `1 hora` e `3 horas`: são estimativa de máquina e
|
||||
viram o campo `minutos_maquina`
|
||||
|
||||
### Fase 2 · Portal e robô — recebe 24h sozinho
|
||||
|
||||
Entrega valor sem o kanban existir.
|
||||
|
||||
- [ ] Contratar VPS, storage S3 e o subdomínio (ver README)
|
||||
- [ ] Webhook do Tiny → cria token → dispara link no WhatsApp
|
||||
- [ ] Página de upload com campo de repetição e prévia da montagem
|
||||
- [ ] URL pré-assinada · upload em partes até **5 GB**
|
||||
- [ ] Pré-flight (`preflight.py` pronto)
|
||||
- [ ] Normalização: vetor → PDF, raster → TIF, CMYK → RGB
|
||||
- [ ] Montagem empilhada · fatiamento em 20 m · carimbo com QR de 20 mm
|
||||
- [ ] ClamAV em segundo plano
|
||||
- [ ] Retenção de 30 dias · artes para recompra, 12 meses
|
||||
|
||||
### Fase 3 · Agente — o arquivo cai na pasta sozinho
|
||||
|
||||
- [ ] Serviço no servidor da fábrica (NSSM ou systemd)
|
||||
- [ ] Webhook + polling de 60 s
|
||||
- [ ] Download com verificação de hash
|
||||
- [ ] Heartbeat a cada 5 min · alerta se sumir por 15
|
||||
|
||||
### Fase 4 · Kanban como aba do PCP
|
||||
|
||||
- [ ] Quadro com as 7 colunas e cronômetro por card
|
||||
- [ ] Painel das 6 máquinas com **indicador vermelho de ocupada**
|
||||
- [ ] `puxar próximo` — reserva de 3 min, um pedido por vez
|
||||
- [ ] `devolver à fila` — volta ao topo
|
||||
- [ ] Campo `minutos_maquina` no card, com sugestão automática
|
||||
- [ ] Retrabalho: abertura, classificação de causa, autorização
|
||||
- [ ] Painel com filtros de período
|
||||
- [ ] Endpoints de relatório: etapas, impressão, aproveitamento, retrabalho
|
||||
|
||||
### Fase 5 · Duas semanas medindo
|
||||
|
||||
**Não pule.** É o que decide se vale o terceiro turno.
|
||||
|
||||
O que sai depois de duas semanas rodando:
|
||||
- tempo real de cada etapa
|
||||
- metros/hora reais por máquina
|
||||
- quanto do tempo do designer é retrabalho de arquivo ruim
|
||||
- retrabalho medido contra o que a sala achava que era
|
||||
|
||||
---
|
||||
|
||||
## Funcionalidades, uma a uma
|
||||
|
||||
### Portal do cliente
|
||||
|
||||
**Link com token.** Um por pedido, UUID v4, 7 dias de validade. O token já
|
||||
carrega o número do pedido no Tiny — o cliente não digita nada.
|
||||
|
||||
**Upload direto para o storage.** URL pré-assinada, em partes, até 5 GB. O
|
||||
arquivo nunca passa pelo VPS.
|
||||
|
||||
**Campo de repetição.** O cliente sobe uma arte e informa quantas vezes repetir.
|
||||
O robô empilha. É o trabalho braçal que hoje o designer faz à mão.
|
||||
|
||||
**Prévia da montagem.** Mostra como a folha ficou antes de fechar o pedido.
|
||||
Montagem **empilhada**, sem encaixe lado a lado — decisão do Marcus.
|
||||
|
||||
**Gabarito 57 × 97 cm** para download. Os 30 mm do rodapé são do carimbo.
|
||||
|
||||
**Resposta na hora.** Aprovado ou recusado com o motivo em português. **O relógio
|
||||
do prazo só começa quando a arte é aprovada.**
|
||||
|
||||
### Robô de pré-flight
|
||||
|
||||
Ordem: **normalizar → validar → repetir → fatiar → carimbar.**
|
||||
|
||||
| Verificação | Limite | Ação |
|
||||
|---|---|---|
|
||||
| Canal de transparência | obrigatório | recusa |
|
||||
| DPI efetivo na medida comprada | < 150 | recusa |
|
||||
| Largura | > 57 cm | recusa |
|
||||
| Formato | JPEG | recusa |
|
||||
| DPI efetivo | 150 a 300 | aceita com aviso |
|
||||
| Alfa parcial nas bordas | > 15% | aceita com aviso |
|
||||
| CDR | — | **entra sem validar**, etiqueta "conferir" |
|
||||
|
||||
**DPI efetivo = pixels ÷ (cm comprados ÷ 2,54).** O metadado do arquivo mente.
|
||||
|
||||
**Normalização por natureza:** vetor sai PDF mantendo vetor, raster sai TIF com
|
||||
alfa. CMYK vira RGB. **O original é sempre preservado** — pedido dos designers.
|
||||
|
||||
### Kanban
|
||||
|
||||
**Sete colunas:** arte recebida, arte tratada, aprovação de cor, fila,
|
||||
imprimindo, correção, finalizado. **Não há coluna de aplicação** — a sala só
|
||||
imprime o filme.
|
||||
|
||||
**Fila por ordem de chegada, sempre.**
|
||||
|
||||
**Puxar próximo.** Um botão por máquina. Reserva por 3 minutos; se não entrar em
|
||||
Imprimindo nesse prazo, volta para a fila. **Um pedido por vez.**
|
||||
|
||||
**Indicador de máquina ocupada.** Círculo vermelho e botão desabilitado. Com seis
|
||||
máquinas vendo o mesmo quadro, é o que evita dois operadores no mesmo arquivo.
|
||||
|
||||
**Devolver à fila.** Volta ao **topo**, não ao fim — o pedido já esperou uma vez.
|
||||
|
||||
**Cronômetro por card.** Tempo parado na coluna atual, em tempo real. Verde até
|
||||
1h, amarelo até 3h, vermelho acima.
|
||||
|
||||
**Trilha por pedido.** Tempo em cada etapa e quanto do total foi só esperando.
|
||||
|
||||
### Estimativa de máquina
|
||||
|
||||
Campo `minutos_maquina` no card. **Herda os marcadores `30 min`, `1 hora` e
|
||||
`3 horas` do Tiny**, que a sala confirmou serem estimativa.
|
||||
|
||||
- O sistema sugere pelo tamanho: `metros ÷ 20 × 60`
|
||||
- Quem trata a arte ajusta **só quando foge do normal**
|
||||
- É esse número que soma a fila contra a capacidade do dia
|
||||
|
||||
### Aprovação de cor
|
||||
|
||||
A sala confirmou que **hoje não manda prova para ninguém**, e que as cores
|
||||
problemáticas são as secundárias: azul, vermelho, castanho, laranja e verde.
|
||||
São cores fora do gamut CMYK — o monitor do cliente mostra o que a máquina não
|
||||
reproduz.
|
||||
|
||||
Dispara sozinho em três casos: arquivo acima de 10 m, primeiro pedido do
|
||||
cliente, ou cor detectada fora do gamut.
|
||||
|
||||
**Custo:** ~30 cm de filme, R$ 1,50. Uma reposição de 20 m custa R$ 99.
|
||||
|
||||
**Sugestão de implantação:** começar só com **cliente novo**. Os outros dois
|
||||
gatilhos entram depois, senão metade dos pedidos vai esperar resposta e o prazo
|
||||
morre.
|
||||
|
||||
### Retrabalho
|
||||
|
||||
SKU `CRRMP.TX.100CM`. **Mayana abre e classifica a causa; Thales ou Alexandre
|
||||
autorizam** — eles confirmaram que acham justo, já que entra na meta deles.
|
||||
|
||||
**A causa fica travada depois de aberta.** Com o indicador atrelado a bônus,
|
||||
haveria incentivo para reclassificar falha de máquina como culpa do cliente.
|
||||
Quem discorda contesta, e a contestação fica registrada.
|
||||
|
||||
**⚠ A meta precisa ser redefinida.** A proposta era 0,4% por causa, somando 1,6%.
|
||||
A sala respondeu que **"meta geral seria melhor"**. Antes de codificar, decidir
|
||||
com o Marcus: meta única sobre o total da casa, ou por causa. O código tem
|
||||
`META_POR_CAUSA` isolado justamente para isso.
|
||||
|
||||
### Integração com o Tiny
|
||||
|
||||
**Três marcadores, não sete.** O kanban é a fonte de verdade; o Tiny mostra o
|
||||
estágio grosso para quem não abre o kanban.
|
||||
|
||||
| Movimento | Marcador |
|
||||
|---|---|
|
||||
| Imprimindo | `DTF-PRODUCAO` |
|
||||
| Correção | `CORRECAO DTF` |
|
||||
| Finalizado | `DTF-PRONTO` |
|
||||
|
||||
640 chamadas de API por dia em vez de 1.500.
|
||||
|
||||
### Mensagens ao cliente
|
||||
|
||||
| Quando | Mensagem |
|
||||
|---|---|
|
||||
| 1 hora sem arte | lembrete com o link · o pedido aparece como *faltando arquivo* |
|
||||
| Arte recusada | motivo em português · o prazo não começou |
|
||||
| Arte aprovada | qualidade em **% e DPI**, metragem, tempo estimado, horário previsto |
|
||||
| Entrou em produção | aviso simples |
|
||||
| Correção | motivo · **única que pede resposta** |
|
||||
| Finalizado | pronto para retirada ou envio |
|
||||
|
||||
---
|
||||
|
||||
## O que ainda não pode ser codificado
|
||||
|
||||
### 1 · O SPOT automático
|
||||
|
||||
**Hipótese, não fato.** Os designers disseram que o SPOT é sempre igual; a sala
|
||||
disse que "é possível criar um perfil só para isso". A documentação do Flexi 22
|
||||
cita *transparency mask*, que gera o branco a partir da transparência de PNG e
|
||||
TIF.
|
||||
|
||||
**Mas ninguém configurou ainda**, e a edição MiniTX é OEM.
|
||||
|
||||
O roteiro de teste está em `dtf-teste-branco-automatico.pdf`. Sete passos, uma
|
||||
máquina, meia hora.
|
||||
|
||||
- **Se funcionar:** o arquivo vai do portal direto para a hot folder e o PC
|
||||
central atende só exceções. A madrugada roda sem ninguém.
|
||||
- **Se não funcionar:** o SPOT segue manual e todo pedido passa pelo PC central.
|
||||
**A conta das 24 horas muda.**
|
||||
|
||||
### 2 · A capacidade do PC central
|
||||
|
||||
A sala confirmou que o Production Manager centraliza várias impressoras, **mas
|
||||
que o PC central precisa de mais capacidade**. E as licenças do Flexi são **por
|
||||
PC** — então centralizar o RIP exige licença lá.
|
||||
|
||||
Levantar antes de decidir: configuração atual do PC central, custo de uma
|
||||
licença adicional, e quanto de memória o RIP consome num trabalho de 15 m.
|
||||
|
||||
### 3 · As regras de choke
|
||||
|
||||
Os designers **não responderam** os valores de choke nem a espessura mínima de
|
||||
traço. Sem isso, a regra proposta — 2 px acima de 2 mm, 1 px de 1 a 2 mm,
|
||||
nenhum abaixo de 1 mm — é chute informado.
|
||||
|
||||
**Só entra em código depois de validada com um arquivo real.**
|
||||
|
||||
### 4 · Metros/hora reais
|
||||
|
||||
Todo o cálculo de capacidade assume **20 m/h por máquina**. A sala não respondeu
|
||||
o valor real, e disse que não tem informação sobre tempos.
|
||||
|
||||
Se o real for 14, a capacidade cai de 60.480 para 42.336 metros/mês e o plano
|
||||
das 24 horas precisa ser refeito.
|
||||
|
||||
---
|
||||
|
||||
## Ressalvas honestas sobre o código
|
||||
|
||||
**`tiny.py` é chute informado.** Estrutura padrão de OAuth2 e endpoints v3, não
|
||||
validado contra a documentação real. Está isolado de propósito: se o endpoint
|
||||
for diferente, muda só ali.
|
||||
|
||||
**`carimbar()` precisa de teste visual.** A lógica está certa, mas posicionamento
|
||||
de texto com pyvips sempre pede ajuste olhando o resultado impresso.
|
||||
|
||||
**O front do kanban usa dados fixos.** Precisa trocar por chamadas a
|
||||
`/api/quadro` e `/api/mover`. O protótipo `dtf-kanban-treino.html` tem o layout e
|
||||
o comportamento final.
|
||||
|
||||
**Nada instalado nos PCs da sala.** Decisão do Marcus. O navegador não abre
|
||||
arquivo no FlexiPRINT — o desenho é miniatura no card mais botão que copia o
|
||||
caminho, e File System Access API para mover arquivo pelo navegador.
|
||||
|
||||
---
|
||||
|
||||
## O que precisa de resposta antes da fase 4
|
||||
|
||||
1. Servidor da fábrica é Windows ou Linux?
|
||||
2. A conta do Tiny já tem aplicação na API v3?
|
||||
3. Confirmar na documentação do Tiny o endpoint de marcadores e o limite de
|
||||
requisições por minuto
|
||||
4. O número do WhatsApp migra para a API oficial da Meta?
|
||||
5. Qual a latência da integração VNDA → Tiny? Define quando o link sai
|
||||
6. Meta de retrabalho: geral ou por causa?
|
||||
7. Configuração e licença do PC central, se for virar servidor de RIP
|
||||
219
docs/historico/IMPLEMENTATION_REPORT.md
Normal file
219
docs/historico/IMPLEMENTATION_REPORT.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Local milestone implementation report
|
||||
|
||||
## Implemented
|
||||
|
||||
- A single `dtf-cloud` Compose project: Site, Kanban, FastAPI Portal/API,
|
||||
PostgreSQL 17, private MinIO S3 storage, isolated ClamAV, and a mock integration
|
||||
outbox/scanning worker. Restricted database and MinIO runtime identities are
|
||||
provisioned by separate one-shot initialization jobs.
|
||||
- Existing Site product UI and commercial functions retained, with a separate
|
||||
local checkout bridge. Server pricing mirrors the four price ladders, all
|
||||
grade discounts, assembly-inclusive rates, minimum, and rounding.
|
||||
- Direct multipart browser uploads, part resumption, owned upload completion,
|
||||
size verification, private five-minute operator downloads, persistent volumes,
|
||||
and 30-day object/one-day incomplete-upload lifecycle rules.
|
||||
- Authenticated manual quote review supplies trusted commercial quantities and
|
||||
grades without adding pre-flight. Immutable server quotes include mock freight;
|
||||
the customer confirms the total and creates an idempotent local paid order.
|
||||
- Shared PostgreSQL orders appear in Kanban. Validated transitions, correction
|
||||
reasons, version checks and movement history persist. Tiny/WhatsApp events use
|
||||
a transactional outbox, retry scheduling, unique event keys, and fake receipts.
|
||||
- Loopback-only published ports; API/worker/database on an isolated network;
|
||||
guards reject production mode, real adapters and nonlocal storage endpoints.
|
||||
All long-running services have health checks. `.env.example` contains disposable local
|
||||
defaults only. No new production credentials or provider endpoints.
|
||||
- Local customer registration/login, scrypt password hashes, revocable database
|
||||
sessions and attempt throttling. Customers see owned orders, timelines, final
|
||||
files and correction requests, and can upload corrections through the portal.
|
||||
- Browser-local unfinished-cart recovery for 24 hours, including file blobs,
|
||||
with storage errors surfaced. Recovered rows can be removed/replaced and new
|
||||
items added. Customer navigation now points to local pages instead of the old
|
||||
external account/cart destinations.
|
||||
- Manual final-file sets cover every order item, support multiple parts, and
|
||||
are required before queue entry. Corrections invalidate old final approvals.
|
||||
Customer and operator file views distinguish originals, corrections and finals.
|
||||
- Worker retention cleanup: one-day unfinished uploads, seven-day originals
|
||||
after final artwork approval, and final/correction files no later than 30 days
|
||||
from the first upload. Combined database/clean-object backup plus isolated,
|
||||
hash-checked restore verification.
|
||||
- Upload extension/size/count quotas, exact signed multipart `Content-Length`,
|
||||
Host/cross-origin rejection, CSP and security headers, escaped filenames, and
|
||||
logout cleanup of browser cart blobs and checkout metadata.
|
||||
- Expiring, revocable HttpOnly operator sessions replace browser-stored Basic
|
||||
credentials. Customer passwords use stronger scrypt parameters, with legacy
|
||||
verification and upgrade on login.
|
||||
- Completed artwork is quarantined until local ClamAV marks it `clean`. Quote,
|
||||
payment, download, final-file approval, queue, and printing gates fail closed.
|
||||
Scanner errors/rejections remain blocked and expire within three days. This is
|
||||
malware scanning only, not print pre-flight.
|
||||
- Structured redacted security logging, a 30-day `security_events` table, live
|
||||
scanner/signature alert summaries, security regressions, exact-runtime Python
|
||||
auditing, and JSON image-scan reports under `output/security/`.
|
||||
- Reproducible Python 3.12 dependency resolution: all 26 direct/transitive runtime
|
||||
packages are pinned with artifact hashes, and image builds enforce those hashes.
|
||||
- A separate network-disabled staging-readiness gate validates non-secret inputs;
|
||||
it is deliberately not an application deployment or provider connection.
|
||||
- A separate production delivery package defines non-root, hash-locked API/web
|
||||
images, one external-secret Portainer Docker Swarm stack, health-monitored
|
||||
rolling updates, commit-SHA rollback, and one Gitea test/scan/publish/webhook
|
||||
workflow matching the established Graphs/ComporHUB operating model. Its
|
||||
preflight deliberately blocks the current local-only source and placeholder
|
||||
inputs; no registry push, Swarm deployment, or real provider call occurred.
|
||||
|
||||
## URLs
|
||||
|
||||
| Component | URL |
|
||||
|---|---|
|
||||
| Site | http://localhost:8080 |
|
||||
| Customer portal | http://localhost:8080/portal.html |
|
||||
| Kanban | http://localhost:8081 |
|
||||
| API health | http://localhost:8000/health |
|
||||
| Local object API and console | http://localhost:9000 · http://localhost:9001 |
|
||||
|
||||
Kanban local login: `operator` / `local-operator-only`.
|
||||
Setup and the complete browser test are in `LOCAL_SETUP.md`.
|
||||
Interactive `/docs` and `/redoc` are disabled.
|
||||
|
||||
## Verification completed
|
||||
|
||||
- `docker compose up --build -d --wait`: all seven long-running services healthy;
|
||||
both initialization jobs exited successfully and published ports are loopback-only.
|
||||
- `python3 -m unittest local.test_pricing -v`: all tests passed, including
|
||||
4,444 comparisons with the actual Site JavaScript calculator.
|
||||
- `python3 -m local.smoke_test`: passed multipart resume/incomplete completion,
|
||||
download byte identity, private bucket and session ownership, input/tamper
|
||||
rejection, four-mode pricing, reviewed corrections, mock freight, concurrent
|
||||
payment idempotency, valid/invalid transitions, history and eight mock receipts.
|
||||
- `node local/browser_test.mjs`: passed actual browser upload, manual review,
|
||||
local payment, cart recovery, final-file approval, customer registration,
|
||||
customer tracking, Kanban state changes and persisted board reload, with no
|
||||
JavaScript exceptions. Screenshots inspected in `output/local/`.
|
||||
- Local test orders are retained in **Finalizado** for inspection, including
|
||||
the browser fixture at **R$21.89** and the four-mode smoke order at **R$537.25**.
|
||||
- Restarts of all long-running containers preserved order snapshots, states, mock
|
||||
receipts and original file bytes; all services returned to healthy.
|
||||
- Explicit guard checks rejected production mode, every real integration adapter,
|
||||
R2 selection and a nonlocal S3 endpoint without contacting external services.
|
||||
- `python3 -m local.workflow_test`: passed account/session isolation, guest
|
||||
migration, revoked-cookie rejection, customer corrections, final revision
|
||||
invalidation, secure file downloads and final-file gating.
|
||||
- `python3 -m local.backup create-and-verify`: the refreshed 2026-09-15 bundle
|
||||
archived 61 clean MinIO objects (58,723,323 bytes) alongside the local database,
|
||||
verified SHA-256 manifests,
|
||||
restored and rehashed every object under an isolated MinIO prefix, restored the
|
||||
database with 16 orders and 107 upload records, then removed all temporary restore
|
||||
targets. The prior database-only backup also passed the legacy verifier.
|
||||
- `docker compose exec -T api python -m local.retention_test`: verified expired
|
||||
object deletion, preservation of unexpired bytes, and retention of upload
|
||||
metadata. Only synthetic retention-test object bytes were cleaned up.
|
||||
- `python3 -m local.security_test`: passed CSP/frame/Host/origin defenses,
|
||||
rejection of Basic credentials, HttpOnly operator-session revocation, extension
|
||||
and multipart-size signing, upload quotas, and login throttling.
|
||||
- `python3 -m local.scanning_test`: a real harmless EICAR fixture was rejected by
|
||||
ClamAV and could not be downloaded or quoted; a clean control was released.
|
||||
- `docker compose exec -T api python -m local.runtime_security_test`: passed
|
||||
database/MinIO least privilege, legacy/current password hashes, quarantine
|
||||
states, live scanner commands, scan-size rejection, and fail-closed offline behavior.
|
||||
- The hash-enforced rebuild completed successfully; `pip check`, four staging-gate
|
||||
regressions, security, smoke, and runtime-security tests passed. A fresh
|
||||
exact-runtime `pip-audit` found no known Python advisories on 2026-09-15.
|
||||
- `node local/browser_test.mjs`: additionally passed filename XSS probes with CSP
|
||||
bypassed, absence of stored operator credentials, and logout removal of File blobs.
|
||||
- The final rebuilt stack has seven healthy long-running services; Site/API routing and a fresh
|
||||
guest portal session were checked after the rebuild.
|
||||
- Production-package validation passed Python/unit and shell syntax checks, Gitea
|
||||
workflow YAML parsing, and `docker stack config` rendering. The API production
|
||||
image built from an immutable Python base. The web production image built from
|
||||
immutable Python/Nginx bases and ran as UID 101 with a read-only filesystem,
|
||||
returning 200 for its configured Host and 400 for an unexpected Host.
|
||||
- The pinned ClamAV validation image started as UID 100:101 with a read-only
|
||||
filesystem, all capabilities dropped, and `no-new-privileges`, then returned
|
||||
`PONG` through the production health command; those restrictions are encoded in
|
||||
the Swarm stack.
|
||||
- `python3 deploy/production_preflight.py --source-only` returned the expected
|
||||
blocked result for local-only adapters/hosts/fake providers and missing Docker
|
||||
secret-file loading. This is verified fail-closed behavior, not production
|
||||
acceptance.
|
||||
|
||||
The final closeout smoke test retained local order #14 in **Finalizado** with
|
||||
eight durable fake receipts. The final workflow test again passed identity,
|
||||
correction, download, invalidation, and final-file gates after the PostgreSQL 17
|
||||
image refresh. Existing database and MinIO named volumes were preserved.
|
||||
|
||||
## Reuse and replacement decisions
|
||||
|
||||
| Existing asset | Decision |
|
||||
|---|---|
|
||||
| `dtf-site.html` | Reused directly. Preserved layout, modes, pricing, client previews, minimums and rounding; added file references and hooks for local checkout/freight. |
|
||||
| `portal/main.py` | Inspected and preserved. Reused FastAPI and direct signed S3 upload design; replaced runtime with `local/app.py` because the prototype starts from Tiny and invokes pre-flight/agent delivery. |
|
||||
| `portal/preflight.py` | Inspected, untouched, never imported by local runtime. |
|
||||
| `kanban/main.py` | Inspected and preserved. Reused workflow state names, movement-history concept and outbox approach; replaced SQLite/folder/machine runtime with PostgreSQL endpoints. |
|
||||
| `kanban/static/kanban.html` | Inspected and preserved. Reused dark palette, cards, columns and drag/drop pattern in `local/static/kanban.html`. Legacy machine controls and conflicting local/server handlers are not loaded. |
|
||||
| Duplicated Tiny/WhatsApp modules | Preserved but excluded from build/runtime. New explicit fake adapters cannot invoke them. |
|
||||
| `agente/` | Inspected, untouched, excluded from build and Compose. Factory automation remains out of scope. |
|
||||
| Root `schema.sql`, `requirements.txt`, `.env.exemplo` | Historical only; local runtime uses a separate `dtf_local` PostgreSQL schema, dependencies, and `.env.example`. No migration of prototype data. |
|
||||
| README/API docs | Legacy content preserved under explicit notices pointing to active local instructions. |
|
||||
|
||||
## Deferred and practical limits
|
||||
|
||||
This is a working local development milestone, not the completed three-week production
|
||||
MVP. Real payments/webhooks, freight providers, Tiny/Olist, WhatsApp, production
|
||||
R2, production account verification/recovery/security, automatic final print-file
|
||||
generation, scheduled/offsite backups and a production restore runbook, and production
|
||||
activation remain undone. Deployment definitions and automation now exist, but
|
||||
their gate correctly prevents use with the local-only application. No factory agent, hot folders, FlexiPRINT, VPN,
|
||||
machine dashboards, label automation, or new automatic pre-flight were added.
|
||||
|
||||
Browser artwork analysis already in the Site is retained as advisory prototype
|
||||
behavior. The backend never adopts its prices, lengths or grades as approved.
|
||||
The temporary manual quote step resolves that trust boundary locally; its role
|
||||
in production needs an explicit product decision. Final files are manually
|
||||
prepared and approved by operators; the test `.cdr` fixture is text, not printable
|
||||
artwork. Cart recovery is browser-local, subject to quota, and does not reconstruct
|
||||
the active artwork editor in place. Fake delivery receipts
|
||||
mean only that the local adapter recorded an event, never that a person received
|
||||
a message or an ERP order was created.
|
||||
|
||||
The multipart transport supports up to 5 GiB, but this local scanner can release
|
||||
only files up to 128 MiB. Larger uploads remain blocked. The scanner has no
|
||||
external network route and uses signatures bundled in its pinned image; rebuild
|
||||
or replace that image before signatures exceed the seven-day alert threshold.
|
||||
|
||||
The 2026-09-15 exact-runtime Python audit reported no known dependency findings.
|
||||
All 26 installed packages are transitively pinned with artifact hashes; scheduled
|
||||
lock refresh and audit automation are still pending.
|
||||
Trivy HIGH/CRITICAL image reports are retained in `output/security/`: API 46
|
||||
(44 Debian records without fixes plus two third-party-SBOM package records absent
|
||||
from the runtime), Site 5, refreshed PostgreSQL 31, pinned MinIO 108, and pinned
|
||||
ClamAV 0. Counts are scanner observations, not exploitability determinations.
|
||||
MinIO was not upgraded over the preserved object volume; its old pinned release
|
||||
is a material localhost-only limitation. See `SECURITY_REPORT.md`.
|
||||
|
||||
The Site retains its pre-existing external fonts/logo/PDF.js 3.11.174 references.
|
||||
The known eval advisory is mitigated by the existing `isEvalSupported:false` call;
|
||||
the old CDN dependency still needs a planned upgrade or vendored/pinned
|
||||
replacement. No new production service references were added. The `.git` directory is unavailable
|
||||
as a working Git repository in this workspace, so changes are delivered as local
|
||||
files without a commit or Git diff.
|
||||
|
||||
## Exact next step
|
||||
|
||||
The localhost security closeout is complete; continue local product work without
|
||||
treating these controls as production approval. Before any staging or production
|
||||
connection, complete `PRODUCTION_INPUTS.md` and pass the network-disabled readiness
|
||||
gate, including the acceptance flow,
|
||||
production authority for length/grade, and freight platform, origin, packaging and
|
||||
policy. The verified local database/clean-object bundle addresses the local
|
||||
recovery gap but is neither scheduled nor offsite and should be created while local
|
||||
writes are idle. Remaining foundations include production backup/restore design,
|
||||
container-image remediation/upgrade decisions, and account verification/recovery
|
||||
design. The current `compose.staging.yaml` validates inputs only and cannot deploy
|
||||
the application or contact providers.
|
||||
Use the checked-in `deploy/` package as the target deployment contract rather
|
||||
than creating another production stack. First add the actual separate staging application runtime, beginning with S3
|
||||
adapter contract tests for direct multipart upload, private downloads, CORS and
|
||||
retention using injected staging credentials. Add real provider adapters only
|
||||
after their contracts are confirmed; payment activation requires signed,
|
||||
idempotent webhooks. Implement Docker-secret file loading, resolve or formally
|
||||
accept image findings, rehearse production restore, and make the release preflight
|
||||
pass without weakening it. The local runtime intentionally refuses production values.
|
||||
26
docs/historico/README.md
Normal file
26
docs/historico/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Historical documents
|
||||
|
||||
These describe the project as it was originally designed: a Tiny-first flow with
|
||||
token upload links, a factory-side agent, a local SQLite Kanban, FlexiPRINT
|
||||
automation, 90-day retention and 12-month artwork reuse.
|
||||
|
||||
**None of it describes the system that exists.** `CONTEXT.md` is the current
|
||||
source of truth and `ROADMAP.md` tracks outstanding work. These are kept because
|
||||
they record decisions and reasoning that the current documents do not repeat —
|
||||
read them for background, never as instructions.
|
||||
|
||||
| File | What it was |
|
||||
|---|---|
|
||||
| `ESPECIFICACAO.md` | Original specification of the Tiny-first model |
|
||||
| `API.md` | Endpoint sketch for the prototype portal |
|
||||
| `TAREFAS.md` | Task plan written before the current scope |
|
||||
| `IMPLEMENTATION_REPORT.md` | Status report for the prototype milestone |
|
||||
| `dtf-organograma-*` | Organisation charts, referenced by nothing |
|
||||
|
||||
The prototype code they describe — `portal/`, `kanban/`, `agente/`, the root
|
||||
`schema.sql` and `.env.exemplo` — was removed once nothing referenced it. Recover
|
||||
it from Git history if it is ever needed:
|
||||
|
||||
```bash
|
||||
git log --oneline --diff-filter=D -- portal kanban agente
|
||||
```
|
||||
212
docs/historico/TAREFAS.md
Normal file
212
docs/historico/TAREFAS.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Tarefas — em ordem de execução
|
||||
|
||||
> Cada tarefa tem **critério de aceite**: o que precisa acontecer para ela ser
|
||||
> considerada pronta. Sem isso, "terminei" vira discussão.
|
||||
>
|
||||
> A ordem importa: as três primeiras não dependem de decisão de ninguém.
|
||||
|
||||
---
|
||||
|
||||
## Bloco A · Pode começar hoje
|
||||
|
||||
Nada aqui depende de resposta pendente.
|
||||
|
||||
### A1 · Limpar os marcadores do Tiny
|
||||
**Aceite:** a lista de sugestão de marcadores só mostra os padrões. Os pedidos
|
||||
antigos mantêm o histórico intacto.
|
||||
|
||||
- Criar `DTF-PRODUCAO`, `DTF-PRONTO`, `DTF-PROVA-COR`
|
||||
- Remover da lista de sugestão: `inicio 14:45`, `inicio13:34`, `1hrs`, `3h`,
|
||||
`+30min de correcao` e as demais variações digitadas à mão
|
||||
- **Não apagar do histórico dos pedidos** — só da lista de opções
|
||||
- **Preservar** `30 min`, `1 hora`, `3 horas`: são estimativa de máquina
|
||||
|
||||
### A2 · Contratar a infraestrutura
|
||||
**Aceite:** `https://arte.dropstaratacado.com.br` responde com certificado
|
||||
válido, e um arquivo de teste sobe e desce do storage.
|
||||
|
||||
- VPS Linux 2 vCPU / 4 GB · Hetzner, Contabo ou Hostinger
|
||||
- Storage S3 compatível · Cloudflare R2 ou Backblaze B2
|
||||
- Subdomínio + Let's Encrypt
|
||||
- Bucket `dtf-artes` com ciclo de vida de 30 dias
|
||||
- Prefixo `artes-cliente/` com 12 meses
|
||||
- CORS liberado só para o domínio do portal
|
||||
|
||||
### A3 · Criar o banco
|
||||
**Aceite:** `schema.sql` roda sem erro no Postgres e no SQLite.
|
||||
|
||||
- Postgres na nuvem · SQLite na fábrica
|
||||
- Dump diário do Postgres para o storage
|
||||
|
||||
### A4 · Provisionar acessos
|
||||
**Aceite:** um token do Tiny obtido por código, e um WhatsApp de teste enviado.
|
||||
|
||||
- Aplicação na API v3 do Tiny · client id e secret
|
||||
- **Confirmar na documentação** o endpoint de marcadores e o limite de req/min
|
||||
- WhatsApp: definir Meta oficial ou provedor
|
||||
|
||||
---
|
||||
|
||||
## Bloco B · Portal — entrega sozinho
|
||||
|
||||
Ao fim deste bloco a Altus **já recebe arte 24 horas**, mesmo sem kanban.
|
||||
|
||||
### B1 · Webhook e link
|
||||
**Aceite:** um pedido criado no Tiny gera link e chega no WhatsApp em menos de
|
||||
1 minuto.
|
||||
|
||||
- `POST /webhook/tiny` → cria pedido, token de 7 dias, dispara WhatsApp
|
||||
- Idempotente: pedido repetido não duplica
|
||||
- **Medir a latência VNDA → Tiny** e registrar
|
||||
|
||||
### B2 · Página de upload
|
||||
**Aceite:** um arquivo de 5 GB sobe sem derrubar o VPS.
|
||||
|
||||
- URL pré-assinada, upload em partes
|
||||
- Campo de repetição com prévia da conta
|
||||
- Gabarito 57 × 97 cm para download
|
||||
- Rate limit de 20 req/min por token
|
||||
|
||||
### B3 · Pré-flight
|
||||
**Aceite:** dos cinco arquivos que os designers separarem, o robô decide igual
|
||||
ao que eles decidiriam.
|
||||
|
||||
- `preflight.py` está pronto — integrar e testar
|
||||
- Normalização: vetor → PDF, raster → TIF, CMYK → RGB, **original preservado**
|
||||
- Repetição, fatiamento em 20 m, carimbo com QR de 20 mm
|
||||
- CDR entra sem validar, etiqueta "conferir"
|
||||
- **`carimbar()` precisa de teste visual impresso** antes de valer
|
||||
|
||||
### B4 · Mensagens
|
||||
**Aceite:** as seis mensagens chegam com o texto certo, e a de correção pede
|
||||
resposta.
|
||||
|
||||
- Lembrete de 1 hora sem arte
|
||||
- Recusa com motivo em português
|
||||
- Aprovação com **% e DPI**, metragem, tempo e horário previsto
|
||||
- Em produção, correção, finalizado
|
||||
|
||||
### B5 · Antivírus
|
||||
**Aceite:** um EICAR de teste é barrado e não chega ao agente.
|
||||
|
||||
- ClamAV em segundo plano, sem travar a fila
|
||||
|
||||
---
|
||||
|
||||
## Bloco C · Agente
|
||||
|
||||
### C1 · Serviço na fábrica
|
||||
**Aceite:** derrubar a internet por 10 minutos e religar — o agente baixa o
|
||||
acumulado sozinho, sem duplicar nada.
|
||||
|
||||
- Windows via NSSM ou Linux via systemd
|
||||
- Webhook + polling de 60 s
|
||||
- Download em arquivo temporário, renomeia só quando completa
|
||||
- Verificação de SHA-256
|
||||
- Idempotente pelo `arte_id`
|
||||
|
||||
### C2 · Monitoramento
|
||||
**Aceite:** matar o processo dispara alerta em até 15 minutos.
|
||||
|
||||
- Heartbeat a cada 5 min
|
||||
- Alerta ao TI se sumir
|
||||
|
||||
---
|
||||
|
||||
## Bloco D · Kanban como aba do PCP
|
||||
|
||||
### D1 · Quadro
|
||||
**Aceite:** dois operadores clicando `puxar` ao mesmo tempo — só um pega.
|
||||
|
||||
- 7 colunas · **sem coluna de aplicação**
|
||||
- Cronômetro por card, em tempo real
|
||||
- Front pronto em `kanban/static/kanban.html`
|
||||
- `?maq=4` abre filtrado na máquina
|
||||
|
||||
### D2 · Puxar e devolver
|
||||
**Aceite:** puxar e não mover em 3 minutos devolve o pedido à fila sozinho.
|
||||
|
||||
- Reserva de **3 min** · **um pedido por vez**
|
||||
- Devolver volta ao **topo**
|
||||
- Círculo vermelho na máquina ocupada, botão desabilitado
|
||||
|
||||
### D3 · Movimento e integração
|
||||
**Aceite:** desligar o Tiny, mover cards, religar — os marcadores entram sozinhos
|
||||
e nenhum movimento se perde.
|
||||
|
||||
- Gravar o movimento **antes** de qualquer chamada externa
|
||||
- Fila de jobs com retentativa em 1, 5 e 30 min
|
||||
- Três marcadores no Tiny, não sete
|
||||
- Arquivo acompanha o card entre as pastas
|
||||
|
||||
### D4 · Estimativa de máquina
|
||||
**Aceite:** o card sugere os minutos e aceita ajuste manual.
|
||||
|
||||
- Campo `minutos_maquina`, sugerido por `metros/20*60`
|
||||
- Importar os marcadores `30 min`, `1 hora`, `3 horas` dos pedidos existentes
|
||||
|
||||
### D5 · Retrabalho
|
||||
**Aceite:** a causa não pode ser alterada depois de aberta, nem pelo autorizador.
|
||||
|
||||
- Abertura com causa obrigatória e evidência
|
||||
- Alçada automática · reincidência sobe nível
|
||||
- Contestação registrada sem alterar a causa
|
||||
- **Meta ainda não definida** — `META_POR_CAUSA` isolado
|
||||
|
||||
### D6 · Painel e relatórios
|
||||
**Aceite:** os quatro relatórios respondem com dados reais depois de uma semana.
|
||||
|
||||
- Filtros de período
|
||||
- `/api/relatorio/etapas`, `/impressao`, `/aproveitamento`, `/retrabalho`
|
||||
- Aproveitamento lendo as transferências do depósito no Tiny
|
||||
|
||||
---
|
||||
|
||||
## Bloco E · Só depois de medir
|
||||
|
||||
**Duas semanas com o kanban rodando antes de tocar nisto.**
|
||||
|
||||
### E1 · Decidir o terceiro turno
|
||||
Depende de: metros/hora reais, tempo entre etapas, e quanto do tempo do designer
|
||||
é retrabalho de arquivo ruim.
|
||||
|
||||
### E2 · SPOT automático
|
||||
Depende do teste em `dtf-teste-branco-automatico.pdf`.
|
||||
|
||||
### E3 · PC central como servidor de RIP
|
||||
Depende de: configuração atual, custo da licença (é **por PC**), e memória que o
|
||||
RIP consome.
|
||||
|
||||
---
|
||||
|
||||
## Bloqueios · o que trava qual tarefa
|
||||
|
||||
| Tarefa | Espera por | De quem |
|
||||
|---|---|---|
|
||||
| B3 · pré-flight | os cinco arquivos de exemplo | designers |
|
||||
| B3 · regras de choke | valor do choke e espessura mínima | designers |
|
||||
| D5 · meta | meta geral ou por causa? | Marcus |
|
||||
| E1 · terceiro turno | metros/hora reais | sala |
|
||||
| E2 · SPOT | resultado do teste | sala |
|
||||
| E3 · RIP central | config e licença do PC central | Wagner |
|
||||
|
||||
**Nada no bloco A, B1, B2, C e D1 a D4 está bloqueado.** Dá para chegar até o
|
||||
kanban funcionando sem nenhuma dessas respostas.
|
||||
|
||||
---
|
||||
|
||||
## Ordem sugerida de trabalho
|
||||
|
||||
```
|
||||
semana 1 A1 · A2 · A3 · A4
|
||||
semana 2 B1 · B2
|
||||
semana 3 B3 · B4 · B5 ← portal no ar, recebendo 24h
|
||||
semana 4 C1 · C2 ← arquivo caindo na pasta sozinho
|
||||
semana 5 D1 · D2
|
||||
semana 6 D3 · D4
|
||||
semana 7 D5 · D6 ← kanban completo
|
||||
semana 8+ medir · depois E
|
||||
```
|
||||
|
||||
**Ao fim da semana 3 a Altus já recebe arte de madrugada e recusa arquivo ruim
|
||||
automaticamente**, sem nada ter mudado na sala. É o primeiro ganho real.
|
||||
BIN
docs/historico/dtf-organograma-sistema.pdf
Normal file
BIN
docs/historico/dtf-organograma-sistema.pdf
Normal file
Binary file not shown.
205
docs/historico/dtf-organograma-ti.html
Normal file
205
docs/historico/dtf-organograma-ti.html
Normal file
@@ -0,0 +1,205 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>DTF — arquitetura para o TI</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{--bg:#0E1013;--card:#191C21;--card2:#1F232A;--linha:#2C3039;--tx:#E9E7E2;--fraco:#8A8F98;
|
||||
--ciano:#00A6C4;--roxo:#7C6BD1;--magenta:#C8256B;--verde:#3E9B62;--alerta:#C9531F;--amarelo:#D9A400}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{background:var(--bg);color:var(--tx);font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:14px;line-height:1.55;-webkit-font-smoothing:antialiased}
|
||||
h1,h2,h3{font-family:"Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em}
|
||||
.faixa{height:5px;background:linear-gradient(90deg,var(--ciano) 0 34%,var(--roxo) 34% 67%,var(--magenta) 67% 100%)}
|
||||
.wrap{max-width:1180px;margin:0 auto;padding:0 20px 70px}
|
||||
header{padding:34px 0 18px}
|
||||
.eb{font-size:10.5px;letter-spacing:.26em;text-transform:uppercase;color:var(--ciano);margin-bottom:10px}
|
||||
h1{font-weight:700;font-size:clamp(26px,4.6vw,44px);line-height:1.03}
|
||||
h1 span{color:var(--fraco)}
|
||||
.lead{color:var(--fraco);max-width:70ch;margin-top:10px}
|
||||
.mapa{margin:24px 0 0;overflow-x:auto}
|
||||
svg{width:100%;min-width:960px;height:auto}
|
||||
.zona{fill:#14161A;stroke:var(--linha);stroke-dasharray:4 3}
|
||||
.zt{font-family:"IBM Plex Mono",monospace;font-size:10px;fill:var(--fraco);letter-spacing:.2em;text-transform:uppercase}
|
||||
.no rect{fill:var(--card);stroke:var(--linha);cursor:pointer;transition:stroke .15s,fill .15s}
|
||||
.no:hover rect{stroke:var(--ciano)}
|
||||
.no[aria-current="true"] rect{fill:var(--card2);stroke-width:2}
|
||||
.no text{pointer-events:none}
|
||||
.nn{font-family:"Space Grotesk",sans-serif;font-size:13.5px;font-weight:700;fill:var(--tx)}
|
||||
.nq{font-family:"IBM Plex Mono",monospace;font-size:9.5px;fill:var(--fraco)}
|
||||
.lig{stroke:var(--linha);fill:none;marker-end:url(#s)}
|
||||
.lb{font-family:"IBM Plex Mono",monospace;font-size:9.5px;fill:var(--fraco)}
|
||||
.det{margin-top:26px;background:var(--card);border:1px solid var(--linha);border-left:3px solid var(--acc,var(--ciano));border-radius:9px;padding:22px 24px}
|
||||
.det h2{font-size:22px;margin-bottom:2px}
|
||||
.det .sub{color:var(--acc,var(--ciano));font-size:12.5px;margin-bottom:16px}
|
||||
.det p{margin-bottom:12px;max-width:74ch}
|
||||
pre{background:var(--card2);border:1px solid var(--linha);border-radius:7px;padding:13px 15px;overflow-x:auto;font-size:12.5px;line-height:1.6;margin:12px 0;color:#C9CDD4}
|
||||
pre b{color:var(--ciano);font-weight:500} pre i{color:var(--fraco);font-style:normal} pre u{color:var(--amarelo);text-decoration:none}
|
||||
.blocos{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;margin-top:14px}
|
||||
.b{background:var(--card2);border:1px solid var(--linha);border-radius:7px;padding:13px 15px}
|
||||
.b h3{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--fraco);margin-bottom:6px;font-family:"IBM Plex Mono",monospace}
|
||||
.b ul{list-style:none} .b li{padding:3px 0 3px 15px;position:relative;font-size:13px}
|
||||
.b li::before{content:"·";position:absolute;left:4px;color:var(--acc,var(--ciano));font-weight:700}
|
||||
.aten{margin-top:14px;border-left:2px solid var(--alerta);background:#22150F;padding:12px 16px;border-radius:0 6px 6px 0;font-size:13.5px}
|
||||
.aten b{color:var(--alerta);display:block;margin-bottom:3px;font-family:"Space Grotesk",sans-serif}
|
||||
code{background:var(--card2);border:1px solid var(--linha);border-radius:4px;padding:1px 6px;font-size:12.5px;color:var(--ciano)}
|
||||
.dica{color:var(--fraco);font-size:12px;margin-top:10px}
|
||||
@media(prefers-reduced-motion:reduce){*{transition:none!important}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="faixa"></div>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<div class="eb">Documento técnico · Wagner · agosto 2026</div>
|
||||
<h1>Arquitetura do DTF 24h<br><span>três zonas, oito componentes</span></h1>
|
||||
<p class="lead">Clique em qualquer caixa para ver o que ela faz, em que linguagem, o que acontece se cair e o que ainda depende de resposta. O código completo está em <code>dtf-sistema.zip</code>.</p>
|
||||
</header>
|
||||
|
||||
<div class="mapa">
|
||||
<svg viewBox="0 0 1120 380" role="img" aria-label="Arquitetura do sistema DTF">
|
||||
<defs><marker id="s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto">
|
||||
<path d="M1 1L9 5L1 9" fill="none" stroke="#2C3039" stroke-width="1.6"/></marker></defs>
|
||||
|
||||
<rect class="zona" x="10" y="26" width="330" height="120" rx="8"/>
|
||||
<text class="zt" x="26" y="46">Terceiros</text>
|
||||
<rect class="zona" x="356" y="26" width="380" height="230" rx="8"/>
|
||||
<text class="zt" x="372" y="46">Nuvem · VPS + storage</text>
|
||||
<rect class="zona" x="752" y="26" width="358" height="330" rx="8"/>
|
||||
<text class="zt" x="768" y="46">Fábrica · rede interna</text>
|
||||
|
||||
<path class="lig" d="M200 84 H424"/><text class="lb" x="250" y="78">webhook</text>
|
||||
<path class="lig" d="M584 110 V140"/>
|
||||
<path class="lig" d="M584 196 V226"/>
|
||||
<path class="lig" d="M700 168 H850"/><text class="lb" x="730" y="162">HTTPS</text>
|
||||
<path class="lig" d="M916 200 V232"/>
|
||||
<path class="lig" d="M916 288 V318"/>
|
||||
<path class="lig" d="M850 262 H620 M620 262 V88 M620 88 H460" style="stroke-dasharray:3 3"/>
|
||||
<text class="lb" x="636" y="180">marcador</text>
|
||||
<path class="lig" d="M424 116 H200" style="stroke-dasharray:3 3"/><text class="lb" x="252" y="132">WhatsApp</text>
|
||||
|
||||
<g class="no" data-i="0" tabindex="0"><rect x="26" y="58" width="174" height="52" rx="8"/>
|
||||
<text class="nn" x="42" y="82">Tiny · Olist</text><text class="nq" x="42" y="98">pedido nasce aqui</text></g>
|
||||
<g class="no" data-i="1" tabindex="0"><rect x="26" y="116" width="174" height="24" rx="6"/>
|
||||
<text class="nn" x="42" y="133" style="font-size:12px">WhatsApp API</text></g>
|
||||
|
||||
<g class="no" data-i="2" tabindex="0"><rect x="424" y="58" width="260" height="52" rx="8" style="stroke:#00A6C4"/>
|
||||
<text class="nn" x="442" y="82">Portal · FastAPI</text><text class="nq" x="442" y="98">token, link, pré-assinada</text></g>
|
||||
<g class="no" data-i="3" tabindex="0"><rect x="424" y="140" width="260" height="56" rx="8" style="stroke:#00A6C4"/>
|
||||
<text class="nn" x="442" y="164">Robô · pyvips</text><text class="nq" x="442" y="182">valida, repete, fatia, carimba</text></g>
|
||||
<g class="no" data-i="4" tabindex="0"><rect x="424" y="226" width="124" height="24" rx="6"/>
|
||||
<text class="nn" x="440" y="243" style="font-size:12px">PostgreSQL</text></g>
|
||||
<g class="no" data-i="5" tabindex="0"><rect x="560" y="226" width="124" height="24" rx="6"/>
|
||||
<text class="nn" x="576" y="243" style="font-size:12px">Storage S3</text></g>
|
||||
|
||||
<g class="no" data-i="6" tabindex="0"><rect x="850" y="142" width="200" height="52" rx="8" style="stroke:#7C6BD1"/>
|
||||
<text class="nn" x="866" y="166">Agente</text><text class="nq" x="866" y="182">baixa e grava na pasta</text></g>
|
||||
<g class="no" data-i="7" tabindex="0"><rect x="850" y="232" width="200" height="52" rx="8"/>
|
||||
<text class="nn" x="866" y="256">Pastas do servidor</text><text class="nq" x="866" y="272">00 … 60 · espelho</text></g>
|
||||
<g class="no" data-i="8" tabindex="0"><rect x="850" y="318" width="200" height="52" rx="8" style="stroke:#C8256B"/>
|
||||
<text class="nn" x="866" y="342">Kanban · FastAPI</text><text class="nq" x="866" y="358">SQLite · fonte de verdade</text></g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="det" id="det"></div>
|
||||
<p class="dica">Setas do teclado navegam entre os componentes.</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const C=[
|
||||
{t:"Tiny · Olist",s:"Sistema que já existe · fonte do número do pedido",acc:"var(--fraco)",
|
||||
p:["O número do Tiny é a chave de tudo. O portal só cria o link depois que o pedido existe lá.","<b>Gatilho é webhook, não polling.</b> O link sai no segundo em que o pedido nasce."],
|
||||
code:`<b>POST</b> /webhook/tiny <i>header: x-token</i>
|
||||
{ "numero":"48213", "cliente":"...", "telefone":"...", "metros":2.4 }`,
|
||||
blocos:[["Marcadores que o sistema escreve",["DTF-PRODUCAO — entrou na máquina","CORRECAO DTF — voltou","DTF-PRONTO — finalizado"]],
|
||||
["Marcadores que ele preserva",["multiempresa, VALE, Troca","BOLETO NEXSTAR SICCOB","Maq 1 a 6 (histórico)"]]],
|
||||
aten:["Confirmar antes de subir","Endpoint exato de marcadores na API v3, limite de requisições por minuto e se o refresh token expira. Tudo isolado em <code>tiny.py</code> — se mudar, muda só ali."]},
|
||||
|
||||
{t:"WhatsApp API",s:"Meta oficial ou provedor · três avisos ao cliente",acc:"var(--fraco)",
|
||||
p:["Aprovada, em produção e finalizada. Mais correção, que é a única que pede resposta. Sete avisos viraria spam e o cliente pararia de ler."],
|
||||
blocos:[["Meta oficial",["Sem risco de bloqueio do número","Exige template aprovado","R$ 100 a 300/mês"]],
|
||||
["Z-API ou Evolution",["Sobe rápido, sem template","Risco de bloqueio","Mais barato"]]],
|
||||
aten:["Decisão pendente","O número do DTF migra para a API oficial ou fica com provedor? Muda o custo e o risco."]},
|
||||
|
||||
{t:"Portal · FastAPI",s:"Python 3.12 · VPS Linux · aberto na internet",acc:"var(--ciano)",
|
||||
p:["Cria o token do link, entrega a URL pré-assinada e recebe o aviso de que o upload terminou.","<b>Fica fora da fábrica de propósito.</b> Se a internet da Altus cair às 3h da manhã, o cliente sobe do mesmo jeito e o agente baixa quando voltar."],
|
||||
code:`<b>GET</b> /arte/{token} <i>página</i>
|
||||
<b>POST</b> /api/arte/{token}/url <i>URL pré-assinada</i>
|
||||
<b>POST</b> /api/arte/{token}/pronto <i>dispara o robô</i>
|
||||
<b>GET</b> /api/artes <i>o agente busca aqui</i>`,
|
||||
blocos:[["Por que pré-assinada",["200 MB pelo VPS derrubaria o processo","O navegador fala direto com o storage","Validade de 1 hora"]],
|
||||
["Token do link",["UUID v4, 7 dias","Um por pedido, nunca reaproveitado","Rate limit de 20 req/min"]]]},
|
||||
|
||||
{t:"Robô · pyvips",s:"Pré-flight, repetição, fatiamento e carimbo",acc:"var(--ciano)",
|
||||
p:["pyvips lê PNG e TIFF de 200 MB em streaming, sem carregar na memória. É o que permite rodar num VPS de 4 GB.","<b>A regra que mais pega arquivo ruim é o DPI efetivo.</b> O valor gravado no cabeçalho mente — o cliente escala a imagem e o programa mantém 300."],
|
||||
code:`dpi = img.height / (cm_uteis / 2.54) <i># não confie no metadado</i>
|
||||
<b>se</b> dpi < 150: recusa
|
||||
<b>se</b> repeticoes > 1: empilhar()
|
||||
partes = fatiar(limite_m=<u>15</u>)
|
||||
carimbar(partes[-1]) <i># só na última</i>`,
|
||||
blocos:[["Recusa",["Sem canal alfa","DPI efetivo abaixo de 150","Largura acima de 57 cm","JPEG ou arquivo corrompido"]],
|
||||
["Aceita com aviso",["DPI entre 150 e 300","Traço abaixo de 0,4 mm","Bordas com alfa parcial"]]],
|
||||
aten:["carimbar() precisa de teste visual","A lógica está certa, mas posicionamento de texto com pyvips sempre pede ajuste olhando o resultado impresso."]},
|
||||
|
||||
{t:"PostgreSQL",s:"Na nuvem · pedidos, artes, partes e jobs",acc:"var(--ciano)",
|
||||
p:["Quatro tabelas: <code>pedido</code>, <code>arte</code>, <code>parte</code> e <code>job</code>.","<b>A fila de jobs é tabela, não broker.</b> <code>SELECT … FOR UPDATE SKIP LOCKED</code> resolve. Redis e Celery são complexidade sem retorno neste volume."],
|
||||
blocos:[["Retentativa",["1 min, 5 min, 30 min","Depois desiste e alerta","O log local grava independente"]],
|
||||
["Backup",["Dump diário para o storage","SQLite local no backup do servidor"]]]},
|
||||
|
||||
{t:"Storage S3",s:"Cloudflare R2 ou Backblaze B2",acc:"var(--ciano)",
|
||||
p:["Escolhido por não cobrar saída de dados — o agente baixa tudo que sobe, e isso sairia caro em provedor que tarifa egress.","<b>Retenção de 90 dias.</b> A arte é propriedade do cliente. Regra de ciclo de vida no bucket faz a limpeza sozinha."],
|
||||
aten:["Antivírus antes de liberar","ClamAV no arquivo recebido antes de o agente baixar. É arquivo de terceiro entrando na rede."]},
|
||||
|
||||
{t:"Agente",s:"Python · serviço no servidor da fábrica",acc:"var(--roxo)",
|
||||
p:["Busca o que foi aprovado e grava em <code>00_ARTE_RECEBIDA</code>. Baixa em arquivo temporário e só renomeia quando completa — o kanban nunca vê arquivo pela metade.","Trata queda de internet como situação normal, não como erro. Para, e volta quando a conexão voltar."],
|
||||
code:`webhook do portal + polling de 60 s <i>(rede de segurança)</i>
|
||||
baixa → confere SHA-256 → insere no SQLite → marca baixada
|
||||
heartbeat a cada 5 min`,
|
||||
blocos:[["Windows",["nssm install DtfAgente","Variáveis via AppEnvironmentExtra"]],
|
||||
["Linux",["dtf-agente.service","Restart=always"]]],
|
||||
aten:["Serviço silencioso parado é pior que erro barulhento","Sem heartbeat por 15 minutos, o portal precisa alertar o TI. Ninguém percebe um agente morto até o cliente cobrar."]},
|
||||
|
||||
{t:"Pastas do servidor",s:"Espelho, não fonte de verdade",acc:"var(--fraco)",
|
||||
p:["Nove pastas numeradas para ordenar sozinhas no explorador. O arquivo acompanha o card.","<b>Se a pasta e o banco divergirem, o banco ganha.</b> Um watchdog opcional captura quem move arquivo direto pelo Explorer e corrige o banco."],
|
||||
code:`00_ARTE_RECEBIDA 10_ARTE_TRATADA 20_FILA_IMPRESSAO
|
||||
30_IMPRIMINDO 40_CORRECAO 50_APLICACAO
|
||||
60_FINALIZADO _ERRO _log`},
|
||||
|
||||
{t:"Kanban · FastAPI",s:"SQLite local · rede interna, sem porta para fora",acc:"var(--magenta)",
|
||||
p:["Mover o card grava o movimento, move o arquivo e enfileira o marcador e o WhatsApp. Ninguém abre o Tiny para atualizar nada.","<b>O movimento é gravado antes de qualquer integração externa.</b> Se o Tiny estiver fora do ar, a medição de tempo já está salva."],
|
||||
code:`<b>POST</b> /api/mover {arte_id, para, usuario, maquina}
|
||||
<b>GET</b> /api/quadro <i>cards com tempo parado em tempo real</i>
|
||||
<b>GET</b> /p/{pedido} <i>o QR do carimbo cai aqui</i>
|
||||
<b>GET</b> /api/relatorio/etapas`,
|
||||
blocos:[["Tabela movimento",["de, para, entrou, saiu, segundos","usuário e máquina","é dela que saem todos os números"]],
|
||||
["O que ela responde",["Tempo médio por etapa","Fila por máquina","Taxa de retrabalho","Se o gargalo é máquina ou designer"]]],
|
||||
aten:["O front ainda não está conectado","O protótipo usa dados fixos. Precisa trocar por chamadas a /api/quadro e /api/mover. Meia hora de trabalho."]}
|
||||
];
|
||||
const nos=[...document.querySelectorAll('.no')], det=document.getElementById('det');
|
||||
function ver(i){
|
||||
const c=C[i];
|
||||
det.style.setProperty('--acc',c.acc);
|
||||
let h=`<h2>${c.t}</h2><div class="sub">${c.s}</div>`+c.p.map(p=>`<p>${p}</p>`).join('');
|
||||
if(c.code) h+=`<pre>${c.code}</pre>`;
|
||||
if(c.blocos) h+='<div class="blocos">'+c.blocos.map(b=>
|
||||
`<div class="b"><h3>${b[0]}</h3><ul>${b[1].map(x=>`<li>${x}</li>`).join('')}</ul></div>`).join('')+'</div>';
|
||||
if(c.aten) h+=`<div class="aten"><b>${c.aten[0]}</b>${c.aten[1]}</div>`;
|
||||
det.innerHTML=h;
|
||||
nos.forEach((n,j)=>n.setAttribute('aria-current',j===i?'true':'false'));
|
||||
}
|
||||
nos.forEach(n=>{
|
||||
n.addEventListener('click',()=>ver(+n.dataset.i));
|
||||
n.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){ver(+n.dataset.i);e.preventDefault();}});
|
||||
});
|
||||
document.addEventListener('keydown',e=>{
|
||||
const at=nos.findIndex(n=>n.getAttribute('aria-current')==='true');
|
||||
if(e.key==='ArrowRight'&&at<nos.length-1){nos[at+1].focus();ver(at+1);e.preventDefault();}
|
||||
if(e.key==='ArrowLeft'&&at>0){nos[at-1].focus();ver(at-1);e.preventDefault();}
|
||||
});
|
||||
ver(0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user